Skip to content

Toolbar and ribbon

Show the built-in flat toolbar or the Excel-style ribbon, and drive host chrome from command state.
On this page

Set config to show the built-in toolbar, or config: { ribbon: true } to show an Excel-style ribbon instead.

Show the ribbon
const grid = createGrid(host, { workbook, config: { ribbon: true } });
console.log(grid.getCommandState("undo").disabled); // true: no history yet
Home Insert Data View
Undo Redo
Undo
Paste ▾
Clipboard
Bold Italic Underline
Font
Result: Home is open above the Budget sheet. Three command groups are excerpted here; Undo and Redo are disabled because there is no edit history. This is a static preview.

The ribbon appears above the grid with the Home, Insert, Data and View tabs.

Try it live → Spreadsheet showcase

Full setup
Grid with the Excel-style ribbon
import { createGrid, initSheetwrite, type Workbook } from "@sheetwrite/core";
import "@sheetwrite/core/styles.css";
const host = document.querySelector<HTMLElement>("#grid");
if (host === null) throw new Error("Missing #grid host");
host.style.height = "480px";
const workbook: Workbook = {
activeSheet: "budget",
sheets: [
{
id: "budget",
name: "Budget",
rowCount: 100,
{ key: "item", header: "Item", width: 200, type: "text" },
{ key: "cost", header: "Cost", width: 120, type: "number" },
],
},
],
};
const grid = createGrid(host, { workbook, config: { ribbon: true } });
console.log(grid.getCommandState("undo").disabled); // true: no history yet

Ribbon

The ribbon has four tabs with Excel's group names, labels and order:

TabGroups and controls
HomeUndo. Clipboard: Paste split button. Font: font name and size boxes, grow and shrink, bold, italic, underline, strikethrough, Borders, Fill Color and Font Color palettes. Alignment: top, middle, bottom, wrap, left, center, right, Merge & Center. Number: format box, currency, percent, comma, more and fewer decimals. Cells: Insert, Delete, Format. Editing: AutoSum, Clear, Sort & Filter, Find.
InsertRows & Columns: Insert (rows above or below, columns left or right), Delete (rows or columns). Links: link form. Notes: note form.
DataSort & Filter: Sort A to Z, Sort Z to A, Clear Filter. Outline: Group and Ungroup rows. Show/Hide: Hide Rows, Unhide Rows, Hide Columns, Unhide Columns.
ViewWindow: Freeze Panes, Freeze Top Row, Freeze First Column, Unfreeze Panes. Zoom: zoom out, current percent, zoom in.

Every control reads the same getCommandState contract as host chrome. It disables while the grid is read-only.

Narrow widths

As the grid narrows, the ribbon shrinks the way Excel does:

  1. Large buttons become small ones.
  2. Labels go.
  3. Whole groups fold into one dropdown button that opens the group in a popup.

Groups fold in Excel order. On Home, Editing folds first, then Cells, Number, Alignment, Clipboard, Font and Undo.

Double-click a tab, or use the arrow at the right, to hide the panel and keep only the tab row. The grid takes the space back.

Keyboard

KeyEffect
TabReaches the tab row. The ribbon is first in the host, so keyboard users reach it before the grid.
Arrow keysMove between tabs, and through menus and lists.
EnterPicks a menu or list item.
EscapeReturns focus to the grid.
Ctrl/Cmd shortcutsStill reach the grid, for example undo. Plain keys stay in the ribbon.

Loading

The ribbon loads with import() as its own chunk, only when a grid asks for it. A grid without the ribbon does not download it; that is about 25 KB less minified JavaScript.

  • The grid reserves the ribbon's height while the chunk loads, so nothing moves when it arrives.
  • A chunk that arrives after the chrome was rebuilt, or after the grid was destroyed, is dropped.
  • If the chunk fails to load, the grid shows the flat toolbar. The load error is still reported as an unhandled promise rejection; the host cannot catch it.

Ribbon outside the grid

The composable shell mounts the same ribbon. Pass ribbon: true to createSpreadsheetShell, or call createRibbon to place it yourself:

Place the ribbon yourself
import { createRibbon } from "@sheetwrite/core/shell";
const ribbonHost = document.createElement("div");
host.before(ribbonHost);
const ribbon = createRibbon(ribbonHost, grid, {
focusGrid: () => host.focus(),
onHeightChange: (height) => console.log(`ribbon is ${height}px tall`),
});
// During teardown; safe to call twice.
RibbonOptions fieldEffect
focusGridReturns keyboard focus to the grid after a pick or Escape. By default the host gets focus.
onHeightChangeCalled with the new height after the ribbon is minimized or expanded.

The ribbon styles itself from the same --sheetwrite-widget-*, --sheetwrite-toolbar-* and --sheetwrite-menu-* properties as the toolbar, so light, dark and host themes apply.

GridConfig (toolbar)

Each flag toggles one control. When config is present, every flag except export and ribbon defaults to true. toolbar: false suppresses the toolbar entirely.

Toolbar flags
createGrid(host, { workbook, config: {} }); // toolbar with the default controls
createGrid(host, { workbook, config: { sort: false } }); // toolbar, no sort controls
createGrid(host, { workbook, config: { export: true } }); // toolbar with CSV and XLSX buttons
createGrid(host, { workbook, config: { toolbar: false } }); // no toolbar
createGrid(host, { workbook }); // no toolbar (config omitted)
FlagTypeDefaultControl
toolbarboolean | ToolbarItem[]true¹Master switch. false removes the toolbar. An item list replaces the default controls.
undobooleantrueUndo / redo buttons (also bound to Ctrl+Z / Ctrl+Shift+Z).
boldbooleantrueBold toggle.
italicbooleantrueItalic toggle.
alignbooleantrueLeft / center / right alignment.
textColorbooleantrueText color picker.
fillColorbooleantrueFill (background) color picker.
borderbooleantrueBorder control.
clearFormatbooleantrueClear formatting.
mergebooleantrueMerge / unmerge selection.
sortbooleantrueSort the selected column ascending / descending.
exportbooleanfalseCSV / XLSX export buttons.
ribbonbooleanfalseShow the Excel-style ribbon instead of the flat toolbar.

¹ "Default true" means: when you supply a config object at all. With no config there is no toolbar.

The underline and strikethrough toggles show when bold or italic is on.

Toolbar actions

Every control acts on the current selection; see Interaction → Selection model. A custom toolbar: ToolbarItem[] list names built-in actions, separators, or onClick(grid) callbacks:

Custom toolbar items
createGrid(host, {
config: {
{ action: "undo" },
{ action: "redo" },
{ action: "separator" },
{ action: "bold" },
{ title: "Clear highlights", icon: "Clear", onClick: (grid) => grid.highlightCells(null) },
],
},
});

Built-in action names: bold, italic, underline, strikethrough, alignLeft, alignCenter, alignRight, alignTop, alignMiddle, alignBottom, wrap, textColor, fillColor, border, clearFormat, merge, unmerge, sortAsc, sortDesc, exportCsv, exportXlsx, undo, redo and separator.

Export buttons

  • The export flag always enables CSV. The XLSX button requires an explicit optional installation and import "@sheetwrite/xlsx/register" before use. The framework packages do not install an XLSX backend.
  • Direct grid.exportXlsx(...) calls reject on failure.
  • Built-in toolbar and context-menu actions show an alert and emit one export-error event for CSV or XLSX failures.
  • grid.actions.exportXlsx() returns a promise that does not reject.
  • Built-in buttons show a busy state and ignore repeated starts while their command is pending.

Host-owned command state

Host chrome can use grid.actions without duplicating selection or history logic. Query one command with grid.getCommandState(name), or subscribe to the complete snapshot through command-state-change (onCommandStateChange in React and Svelte, @command-state-change in Vue):

Accessible host toolbar
undoButton.disabled = undo.disabled;
boldButton.disabled = bold.disabled;
boldButton.setAttribute(
"aria-pressed",
bold.activity === "mixed" ? "mixed" : String(bold.activity === "active"),
);
};
const stop = grid.on("command-state-change", ({ states }) => {
update(states.bold, states.undo);
});
boldButton.addEventListener("click", () => grid.actions.toggleBold());
undoButton.addEventListener("click", () => grid.actions.undo());
// Run during host teardown.
stop();
GridCommandState fieldMeaning
disabledAccounts for read-only mode, empty selections, and undo/redo history.
activity"inactive", "active" or "mixed" across the current selection, for formatting commands.

The built-in toolbar uses the same contract, including native disabled and aria-pressed="mixed".

Feature flags

These GridConfig fields enable behavior outside the toolbar row:

FieldTypeDefaultEffect
findbooleantrueBuilt-in Ctrl+F search box, next/previous controls, and live match count. See Search.
contextMenuboolean | ContextMenuItemstrueBuilt-in right-click menu, disabled menu, static readonly rows, or a context-aware row factory. See Context menu.
iconsPartial<Record<ToolbarActionName, ToolbarIcon>>undefinedBuilt-in toolbar icon overrides. Strings render as text; a DOM Node or () => Node supports SVG/HTML without innerHTML.
tabsbooleantrueBottom sheet-tab bar for multi-sheet workbooks.
keyboardboolean | KeyboardOptions | functiontrueBuilt-in key bindings. See Interaction → Keyboard navigation.

Undo, redo and find are keyboard-driven and work without the toolbar: Ctrl+Z undoes the last edit, Ctrl+Shift+Z redoes it, and Ctrl+F opens the find widget (unless find: false).

Edge cases and limits

  • The per-control flags apply to the flat toolbar only, not to the ribbon.
  • A custom toolbar: [...] item list still renders the flat toolbar, even with ribbon: true.
  • toolbar: false removes both the toolbar and the ribbon.
  • Command-state aggregation is bounded. Very large selections conservatively report mixed instead of forcing an unbounded cell walk.
  • grid.setConfig(config) reconfigures the built-in chrome without replacing the grid or clearing selection and history.

See also

Documentation