---
title: "JavaScript Toolbar"
description: "The LemonadeJS Toolbar block for JavaScript: Icon and action toolbar. Contract-verified, framework-agnostic, zero dependencies, with a live example."
source: https://lemonadejs.com/docs/plugins/toolbar/
---

<link rel="stylesheet" href="/v6/contextmenu.css">
<link rel="stylesheet" href="/v6/modal.css">
<link rel="stylesheet" href="/v6/color.css">
<link rel="stylesheet" href="/v6/toolbar.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined">

# JavaScript Toolbar

`@lemonadejs/toolbar` · ✓ 10 contract checks · framework-agnostic · zero dependencies

`<Toolbar />` — a flat action bar, ported faithfully from the v5 plugin.

Three positions (v5 data-position): the default is a fixed bottom app
bar (mobile pattern), 'static' is an inline editor bar, 'left' is a
vertical rail. Three item kinds:

  - regular items: `<a>` with optional image / material icon / title,
    route (href), selected and visible flags
  - dividers (v5 type 'divisor' — both spellings accepted)
  - 'select' pickers: a header that opens a dropdown right under
    itself, composed ON the Contextmenu block exactly like v5
    (<lm-contextmenu :ref="self.menu">); options are Contextmenu
    items, plain strings normalize to { title }

v5 → v6 mapping: data/HTML-children extraction → options array;
item.onclick (declared in the v5 data model but never wired in the
dist template) now fires; the dead v5 onchange/onload params became
real events — onchange fires when a picker option is chosen,
onitemclick (not "onclick": the name would collide with the native
click event on web-component hosts) fires on any item activation.
v5's data-gap CSS hook (left rail spacer) gets its missing template
plumbing via item.gap. One Contextmenu is shared by all pickers
(v5 mounted one per picker), so hovering another picker moves the
open dropdown instead of stacking menus.

Editor-host additions (the Editor block drives its bar through these):
  - item flags are LIVE: mutate selected / disabled / visible / title
    on the item objects, then api.refresh() — the bar patches the
    affected attributes in place (no rebuild, keyed by item identity).
    A caret move updating twelve toggle states costs twelve attribute
    writes, not a bar teardown.
  - item.tooltip: hover text for icon-only items (title renders as a
    visible label, so icon bars need a separate hover string)
  - type 'color': a swatch item that opens the Color block (grid +
    spectrum panel) in a small popover under the item. A pick lands on
    item.value (swatch underline), fires item.onchange(value, item)
    and the bar-level onchange(e, item, { value }), then closes.
    Outside mousedown and Escape dismiss.

## Example

<!--example-->

```js
import { html } from 'lemonadejs';
import Toolbar from '@lemonadejs/toolbar';

const App = (props, { state }) => {
    const last = state('nothing yet');

    const items = [
        { icon: 'undo', tooltip: 'Undo', onclick: () => (last.value = 'undo') },
        { icon: 'redo', tooltip: 'Redo', onclick: () => (last.value = 'redo') },
        { type: 'divider' },
        { type: 'select', title: 'Verdana', options: ['Verdana', 'Arial', 'Georgia', 'Courier New'] },
        { type: 'divider' },
        { icon: 'format_bold', tooltip: 'Bold', selected: true, onclick: () => (last.value = 'bold') },
        { icon: 'format_italic', tooltip: 'Italic', onclick: () => (last.value = 'italic') },
        { icon: 'format_underlined', tooltip: 'Underline', disabled: true },
    ];

    return html`<div>
        <${Toolbar} position="static" options="${items}"
            onchange="${(e, item, option) => (last.value = 'font = ' + option.title)}" />
        <p>Last action: <b>${last}</b></p>
    </div>`;
};
```

## Installation

```bash
npm install @lemonadejs/toolbar
```

```js
import Toolbar from '@lemonadejs/toolbar';
import '@lemonadejs/toolbar/style.css';
```

The icons come from Google Material Symbols. Load the font once per page:

```html
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined">
```

Three deployment forms, one component:

```js
html`<${Toolbar} />`                       // by value (no registration)
setComponents({ Toolbar });               // then <Toolbar /> by name anywhere
createWebComponent(Toolbar);              // <lm-toolbar> in plain HTML/any framework
```

## Props

Every declared prop arrives as a **live state** — pass a value for a snapshot or a
state for a two-way live wire. Attribute strings are coerced to the declared type.

| Prop | Type | Default | Description |
|---|---|---|---|
| `options` | array | — | ToolbarItem[] |
| `position` | string | `''` | '' = fixed bottom bar (v5 default) | 'static' | 'left' |
| `visible` | boolean | `true` | false hides the whole bar |

## Events

All event names are lowercase (the platform convention — LJS-305 warns otherwise).

- `onitemclick` — (e, item, index) on any item activation
- `onchange` — (e, item, option) when a picker option is chosen, (e, item, { value }) on a color pick

## API

```js
import { ref } from 'lemonadejs';
const toolbar = ref();
html`<${Toolbar} ref="${toolbar}" />`;
// toolbar.current.open(...)  ·  toolbar.current.close(...)  ·  toolbar.current.refresh(...)
```

- `open()`
- `close()`
- `refresh()`

## Styling

All classes follow the `lm-toolbar-*` convention; visual variants are `data-*`
attributes on the root. Override freely — there is no styling engine to fight.

## Contract

The machine-readable schema ships with the package:

```js
import contract from '@lemonadejs/toolbar/contract.json';
```

`verify.json` carries the conformance proof produced by `verify(Toolbar)`.

Looking for the v5 plugin? See the [archived v5 documentation](/docs/v5/plugins/).