Ribbon toolbar
Pass toolbar to Spreadsheet.mount() and the ribbon is mounted in the same call, above the formula bar:
import '@libraz/formulon-cell/styles.css'
import { Spreadsheet, type RibbonTab } from '@libraz/formulon-cell'
const instance = await Spreadsheet.mount(host, {
ui: { profile: 'excel365' },
toolbar: {
activeTab: (localStorage.getItem('ribbon-tab') as RibbonTab | null) ?? 'home',
onTabChange: (tab) => localStorage.setItem('ribbon-tab', tab),
},
})
instance.toolbar?.setActiveTab('data')toolbar: true uses the defaults. An options object is a MountToolbarOptions, the same type Spreadsheet.mountToolbar() takes. The mounted ribbon is available as instance.toolbar, a ToolbarInstance; it is null when no ribbon is mounted. When toolbar is omitted, the ui profile decides whether a ribbon appears; see Options.
Like the rest of formulon-cell, the ribbon is beta. Its tabs follow a desktop spreadsheet, but individual commands, dropdowns, and keyboard behavior are not guaranteed to match one. Evaluate it as an integration surface for your application rather than a finished end-user toolbar.
Inline or separate ribbon
Inline: toolbar mount option | Separate: Spreadsheet.mountToolbar() | |
|---|---|---|
| Placement | First child of the spreadsheet host, above the formula bar. | Any element the host owns, such as a title bar or an application header. |
| Handle | instance.toolbar | The returned ToolbarInstance. |
| Runtime changes | instance.setToolbar(next) disposes the ribbon and mounts it again; false removes it. | Call dispose() and mount again. |
| Dropdown menus | On while no interaction policy is active; setPolicy() rebuilds the ribbon to match. | Off until you pass dynamicDropdowns. |
| Accent color | Follows the grid's --fc-accent. | Uses the toolbar defaults; set --fc-tb-* on a wrapper. |
| Disposal | Released by instance.dispose(). | Call toolbar.dispose() before disposing the instance. |
Use the separate form when the ribbon belongs somewhere else in the layout:
import { Spreadsheet } from '@libraz/formulon-cell'
const instance = await Spreadsheet.mount(sheetHost, { ui: { profile: 'excel365' }, toolbar: false })
const toolbar = Spreadsheet.mountToolbar(toolbarHost, instance, {
dynamicDropdowns: true,
onCommand: (id, applied) => {
if (!applied) handleHostCommand(id)
},
})
function close() {
toolbar.dispose()
instance.dispose()
}mountToolbar(host, instance, options) throws when host or instance is missing. The second argument may also be a getter, () => SpreadsheetInstance | null, so the ribbon can render before an asynchronous mount finishes. The getter is read on every dispatch; call toolbar.rerender() once it starts returning the instance so the ribbon subscribes to that instance's state.
The ribbon shell is a flex item. The framework components render a display: contents wrapper so the ribbon takes part in the parent's flex column; give a vanilla toolbarHost the same treatment, or make it a flex column itself.
React and Vue components
React exports SpreadsheetToolbar from @libraz/formulon-cell-react. Vue publishes it as a single-file component under the @libraz/formulon-cell-vue/toolbar.vue subpath, so it compiles with the application's own Vue pipeline. Both wrap Spreadsheet.mountToolbar() and take the instance from onReady or the ready event.
import { Spreadsheet } from '@libraz/formulon-cell'
const instance = await Spreadsheet.mount(sheetHost, { ui: { profile: 'excel365' }, toolbar: false })
const toolbar = Spreadsheet.mountToolbar(toolbarHost, instance, {
activeTab: 'home',
dynamicDropdowns: { applyProtectAction: (action) => openProtectDialog(action) },
hooks: { review: { spelling: () => openSpellChecker() } },
onTabChange: (tab) => saveActiveTab(tab),
})import { useState } from 'react'
import {
Spreadsheet,
SpreadsheetToolbar,
type RibbonTab,
type SpreadsheetInstance,
type ToolbarInstance,
} from '@libraz/formulon-cell-react'
// Module constants keep the references stable; a new object re-mounts the ribbon.
const ui = { profile: 'excel365' } as const
const dropdownActions = {
applyProtectAction: (action: string) => openProtectDialog(action),
}
export function Editor() {
const [instance, setInstance] = useState<SpreadsheetInstance | null>(null)
const [tab, setTab] = useState<RibbonTab>('home')
const [toolbar, setToolbar] = useState<ToolbarInstance | null>(null)
return (
<div className="editor">
<SpreadsheetToolbar
instance={instance}
activeTab={tab}
onTabChange={setTab}
locale="en"
dropdownActions={dropdownActions}
onSpellingReview={() => openSpellChecker()}
onToolbarReady={setToolbar}
/>
<Spreadsheet ui={ui} toolbar={false} onReady={setInstance} />
</div>
)
}<script setup lang="ts">
import { shallowRef, ref } from 'vue'
import {
Spreadsheet,
type RibbonTab,
type SpreadsheetInstance,
} from '@libraz/formulon-cell-vue'
import SpreadsheetToolbar from '@libraz/formulon-cell-vue/toolbar.vue'
const ui = { profile: 'excel365' } as const
const dropdownActions = {
applyProtectAction: (action: string) => openProtectDialog(action),
}
const instance = shallowRef<SpreadsheetInstance | null>(null)
const tab = ref<RibbonTab>('home')
</script>
<template>
<div class="editor">
<SpreadsheetToolbar
:instance="instance"
:active-tab="tab"
locale="en"
:dropdown-actions="dropdownActions"
:on-spelling-review="openSpellChecker"
@tab-change="tab = $event"
/>
<Spreadsheet :ui="ui" :toolbar="false" @ready="instance = $event" />
</div>
</template>instance, activeTab, and locale are required; React also requires onTabChange, and Vue reports tab changes as the tab-change event. instance may be null until the spreadsheet is ready. The optional props are:
| Prop | Use |
|---|---|
ribbonTabs | The tab set; see Tabs and display. |
dropdownActions | Replace individual dropdown handlers; see Dropdown handlers. |
onToolbarReady | Receives the ToolbarInstance, and null when the ribbon is torn down or fails to mount. |
onError | Called when the ribbon fails to mount. Vue also emits error. |
onSpellingReview, onAccessibilityCheck, onTranslate, onRunScript, onAddIn, onDrawPen, onDrawEraser | Host callbacks; see Host callbacks. |
The components set the ribbon language from locale and mount again when instance, locale, dropdownActions, or ribbonTabs changes. A changed activeTab is forwarded to setActiveTab() without a re-mount. Pass dropdownActions and ribbonTabs as stable references (a module constant, useMemo, or a value defined once in setup).
MountToolbarOptions
Every field is optional, so {} is a valid options object.
Platform and labels
| Field | Meaning |
|---|---|
platform | default, mac, or auto. When omitted, the toolbar inherits data-fc-platform from its host, the nearest .fc-host, or the instance host, and follows later platform changes on the instance. |
lang | Locale for built-in ribbon labels. Defaults to the instance locale and follows setLocale(); an explicit lang stays fixed. |
text, menuText | Replace the derived ToolbarText (button titles, group names) and ToolbarMenuText (dropdown labels). |
Tabs and initial state
| Field | Default | Meaning |
|---|---|---|
activeTab | 'home' | Initial RibbonTab. A tab outside the tab set falls back to the first tab. |
ribbonTabs | see Tabs and display | The tab set to render. |
ribbonDisplayMode | 'full' | full, singleLine, tabsOnly, or autoHide. |
collapsed | false | Shorthand for tabsOnly when ribbonDisplayMode is omitted. |
formulaBarVisible | true | Initial state of the View tab's formula bar toggle. |
theme | 'paper' | The ribbon's own paper, ink, or contrast state, reported through onThemeChange. Change the spreadsheet theme with instance.setTheme(). |
borderStyle, borderColor | 'thin', '#000000' | Initial line style and color for the borders menu. |
Command wiring
| Field | Meaning |
|---|---|
hooks | RibbonHooks groups for commands that call into host or feature code: clipboard, sortFilter, insert, page, drawing, review, protection, automation, formula, sheetView. Each group is merged over the built-in group, so a host can replace one handler and keep the rest. |
dynamicDropdowns | Turn on the built-in dropdown menus: true for the defaults, a partial DynamicDropdownsCtx to replace individual handlers, or a function returning that partial for a context built after mount. |
interceptCommand(id, button, event) | Runs before a command is dispatched. Return true to mark it handled and skip the built-in dispatch. |
commandDelegation | Set false when the host handles [data-ribbon-command] clicks itself. Tab and display-option clicks are always handled. |
menus | RibbonMenus submenu factories, merged over the defaults. |
helpers | A partial RibbonRenderHelpers to replace one renderer factory, such as the color picker. |
createBackstageView | Returns the element shown while the backstage is open. |
Runtime hooks
focusSheet, refreshCells, refreshZoom, showMessage, and applyRibbonFormat replace steps the dispatcher runs around a command. By default focusSheet focuses the spreadsheet host, applyRibbonFormat records the format change in the instance history, and the other three do nothing. projectFormatToolbar is called after the ribbon updates its active-state buttons, for hosts that mirror that state elsewhere. Most hosts leave all six unset.
Callbacks
| Callback | Fires after |
|---|---|
onTabChange(tab) | The active tab changes. |
onCollapsedChange(collapsed) | The ribbon switches between expanded and collapsed display modes. |
onDisplayModeChange(mode) | The display mode changes. |
onBackstageOpenChange(open) | setBackstageOpen() changes the backstage state. |
onThemeChange(theme) | The ribbon's theme state changes. |
onFormulaBarChange(visible) | The formula bar toggle changes. |
onCommand(id, applied) | A command was dispatched. applied is false when no built-in handler matched, which lets the host handle its own command IDs as a fallback. |
Selecting the File tab only switches the active tab and fires onTabChange('file'); the host renders its own backstage from that callback. toolbar.setBackstageOpen(true) shows the view returned by createBackstageView, and Back or Escape closes it and restores the previously active tab. Other backstage actions are the host's to handle. A host with a file menu can connect the two:
const toolbar = Spreadsheet.mountToolbar(toolbarHost, instance, {
createBackstageView: () => buildFileMenu({ onClose: () => toolbar.setBackstageOpen(false) }),
onTabChange: (tab) => {
if (tab === 'file') toolbar.setBackstageOpen(true)
},
})Without createBackstageView, an open backstage shows an empty element.
ToolbarInstance
| Member | Use |
|---|---|
host | The element the ribbon was mounted into. |
instance | The current SpreadsheetInstance, or null while a getter returns null. |
applyCommand(id) | Dispatch a ribbon command ID through the same path as a click. Returns true when a handler matched. A command the interaction policy denies also returns true, without running. |
focusActiveTab() | Focus the active tab button, for F6-style landmark cycling. Returns whether focus moved. |
setActiveTab(tab), getActiveTab() | Change or read the active tab. A tab that is not rendered is ignored. |
setDisplayMode(mode), getDisplayMode() | Change or read the display mode. |
setCollapsed(collapsed), getCollapsed() | Switch between tabsOnly and full; getCollapsed() is also true in autoHide. |
setBackstageOpen(open), getBackstageOpen() | Show or hide the backstage view. |
setDisplayMenuOpen(open) | Open or close the ribbon display options menu. |
setFormulaBarVisible(visible), getFormulaBarVisible() | Update the formula bar toggle state. |
setTheme(theme), getTheme() | Update the ribbon's theme state. |
setBorderStyle(style), getBorderStyle(), setBorderColor(color), getBorderColor() | Border menu state. |
dropdownsApi | The dropdown controller when dynamicDropdowns is on, otherwise null. |
rerender() | Rebuild the ribbon. Use it after a getter starts returning an instance; selection and format changes update the ribbon without it. |
dispose() | Remove listeners, restore the host's lang and data-fc-platform, and clear the host element. |
Users can also change the display mode by double-clicking the active tab or pressing Ctrl+F1. Arrow keys, Home, and End move between tabs.
Tabs and display
RibbonTab is one of file, home, insert, draw, pageLayout, formulas, data, review, view, help, automate, and acrobat, plus the contextual tabs chartDesign, chartFormat, shapeFormat, pictureFormat, and tableDesign. The package exports these tab sets:
| Constant | Tabs |
|---|---|
EXCEL365_STANDARD_RIBBON_TABS | File, Home, Insert, Page Layout, Formulas, Data, Review, View, Help. |
OPTIONAL_RIBBON_TABS | Draw, Automate, Acrobat. |
RIBBON_TABS | The standard set followed by the optional tabs. |
EXCEL365_MAC_RIBBON_TABS | Home, Insert, Draw, Page Layout, Formulas, Data, Review, View, Automate. |
CHART_CONTEXTUAL_RIBBON_TABS | Chart Design, Format. |
SHAPE_CONTEXTUAL_RIBBON_TABS | Shape Format. |
PICTURE_CONTEXTUAL_RIBBON_TABS | Picture Format. |
TABLE_CONTEXTUAL_RIBBON_TABS | Table Design. |
CONTEXTUAL_RIBBON_TABS | All of the contextual tabs above. |
When ribbonTabs is omitted, the default platform renders EXCEL365_STANDARD_RIBBON_TABS and the Mac platform renders EXCEL365_MAC_RIBBON_TABS; the React and Vue components share this default. The Draw, Automate, and Acrobat tabs need an explicit RIBBON_TABS or [...EXCEL365_STANDARD_RIBBON_TABS, ...OPTIONAL_RIBBON_TABS], and should be added only when the host has wired what they need. An explicit ribbonTabs also replaces the Mac tab set. Tabs render in the order of the array, and keyboard navigation skips tabs that are hidden or disabled.
buildRibbonModel(lang, { tabs, profile, contextualTabs }) returns the same model the ribbon renders: tabs with an id and label, groups with a title, and commands with an id, label, title, and optional disabledReason. profile is a RibbonProfile, either default or excel365Mac. contextualTabs takes a list of contextual tabs, such as CHART_CONTEXTUAL_RIBBON_TABS (see Contextual tabs), and appends them to the model. Use it to build a host-owned command palette or to look up the command IDs to pass to applyCommand():
import { buildRibbonModel, type RibbonProfile } from '@libraz/formulon-cell'
const profile: RibbonProfile = 'excel365Mac'
const commands = buildRibbonModel('en', { profile }).flatMap((tab) =>
tab.groups.flatMap((group) => group.commands.map((command) => command.id)),
)Contextual tabs
The ribbon adds tabs after the regular ones while an object is selected, on both platforms and whatever ribbonTabs is. Selecting an object replaces the cell selection, so the tabs follow the selection:
| Selection | Tabs | Commands |
|---|---|---|
| Chart | Chart Design, Format | Chart Design: Add Chart Element, Change Colors, Switch Row/Column, Select Data, Change Chart Type. Format: Format Selection and Arrange. |
| Shape | Shape Format | Shape Fill, Shape Outline (color, Weight, Dashes), and Arrange. |
| Picture | Picture Format | Arrange. |
| Cell inside a table | Table Design | Table Name, Header Row, Total Row, Banded Rows, First Column, Last Column, Banded Columns. |
Arrange is one group shared by the chart, shape, and picture tabs: Bring Forward, Send Backward, and Selection Pane act on the selected object. Inserting or pasting an object selects it and opens its first tab; deselecting it returns to the tab that was active before. Color menus show the workbook theme's colors. A host can open the selected chart's settings with instance.openChartSettings(tab?), which takes 'elements' (default), 'styles', or 'filters' and returns false when no chart is selected.
Dropdown handlers
Dropdown menus such as Paste, Fill, Clear, Sort & Filter, and Protect run handlers from a DynamicDropdownsCtx. Override one with a partial object: dynamicDropdowns in mountToolbar() and the inline toolbar option, or dropdownActions on the framework components. Keys you do not pass keep the built-in handler; createDefaultDynamicDropdownsCtx() returns the full default context.
const toolbar = Spreadsheet.mountToolbar(toolbarHost, instance, {
dynamicDropdowns: {
// 'protect-sheet', 'unprotect-sheet', 'protect-workbook', 'unprotect-workbook', 'allow-edit-ranges'
applyProtectAction: (action) => openProtectDialog(action),
},
})The handler names follow the menus: applyCopyAction, applyRibbonPasteAction, applyFillSeries, applyClearAction, applySortMenuAction, applyProtectAction, applyCalcOptionAction, applyScriptAction, insertPictureFromRibbon, createTableFromSelection, and so on. The DynamicDropdownsCtx type declaration lists them all.
The inline ribbon leaves dropdowns off while an interaction policy is active. A mounted ribbon also disables the commands the active policy denies; see Options.
Host callbacks
The framework components route a few ribbon buttons to props:
| Prop | Ribbon command | Core hook |
|---|---|---|
onSpellingReview | Review → Spelling | hooks.review.spelling |
onAccessibilityCheck | Review → Check Accessibility | hooks.review.accessibility |
onTranslate | Review → Translate | hooks.review.translate |
onDrawPen, onDrawEraser | Draw → Pen, Eraser on the default platform | hooks.drawing.setInkMode() with 'pen' or 'erase' |
onRunScript | The custom-script item in the Automate script menu | applyScriptAction('custom') |
onAddIn | Manage in the add-ins menu | applyAddInAction('manage') |
A provided prop replaces the matching core hook. When a prop is omitted, Spelling, Translate, and Check Accessibility keep their built-in handlers, which show a report for the selected cells, and the Script and Add-ins items keep the core script and add-in actions; with vanilla mountToolbar(), pass hooks.review to replace the review handlers. A provided onRunScript or onAddIn handles only its own menu item. The Script and Add-ins buttons open their menu first, and the callback runs when the user picks the matching item. A dropdownActions entry for applyScriptAction or applyAddInAction takes precedence over these props.
Platform differences
On the Mac platform the ribbon uses EXCEL365_MAC_RIBBON_TABS, which has no File or Help tab and includes Draw and Automate. Its Home tab is laid out like the desktop Mac ribbon, with icon-and-text Cut, Copy, and Format Painter rows and large Insert, Delete, and Format tiles. Both Home ribbons put Phonetic Settings in the font group. Mac commands use IDs in the mac. namespace, such as mac.data.goalSeek, mac.insert.sparkline, mac.review.stats, and mac.formulas.insertFunction. applyCommand() dispatches them like any other ID, and many of them reuse the default command behind them.
instance.toolbar?.applyCommand('mac.data.goalSeek')The Mac Draw tab keeps its ink in the mounted session; the drawing hook and the onDrawPen / onDrawEraser props apply to the default platform's Draw tab. Platform and Mac UI lists the Mac dialogs, their command IDs, and their limits.
Styling
@libraz/formulon-cell/styles.css imports the ribbon stylesheet along with the grid and themes, so most applications need nothing else. @libraz/formulon-cell/styles/toolbar.css is the ribbon stylesheet alone. The adapters' @libraz/formulon-cell-react/toolbar.css and @libraz/formulon-cell-vue/toolbar.css re-export that file for applications that list their stylesheets per package.
The ribbon reads its own --fc-tb-* tokens. Under .fc-host, which covers the inline ribbon, --fc-tb-accent and --fc-tb-accent-strong follow the grid's --fc-accent and --fc-accent-strong, so one brand override themes both. A separate toolbar keeps the toolbar defaults; set the tokens on its wrapper:
.app-toolbar {
--fc-tb-accent: #0f766e;
--fc-tb-accent-strong: #115e59;
--fc-tb-ribbon-bg: #ffffff;
--fc-tb-ribbon-hover: #f0fdfa;
}The ink and contrast themes supply their own --fc-tb-* values through data-fc-theme. Theming covers the grid tokens.
Related pages
- Options — when the ribbon appears, and how a policy restricts it.
- Platform and Mac UI — the Mac ribbon, its dialogs, and their limits.
- React and Vue adapters — the
Spreadsheetcomponent props. - Embedding — host-owned controls without the ribbon.
- Theming — themes and grid tokens.