Handsontable with Base Web
This tutorial shows you how to integrate Handsontable into a React app that uses Base Web, mapping Base design tokens to Handsontable colors and tokens so the grid follows your design system.
Overview
This recipe shows how to integrate Handsontable into a React app that uses Base Web. You register a custom Handsontable theme that maps Base Web design tokens to Handsontable colors and tokens, so the grid follows your system styles.
Difficulty: Beginner
Time: ~20 minutes
Stack: React, Base Web, Styletron, Handsontable, @handsontable/react-wrapper
What You’ll Get
- A Handsontable grid with a custom theme registered through
registerTheme('base-data-grid', { icons, colors, tokens }). - A color mapping layer that uses your Base token variables, such as
--bds-primary-a,--bds-background-primary, and--bds-content-primary. - Theme token overrides for spacing and radius, so the grid matches Base Web’s component shape and density.
Prerequisites
- A React app with Base Web set up (including
BaseProviderand Styletron engine). - Base design tokens exposed as CSS variables in your global stylesheet.
- Handsontable and the React wrapper installed.
Install dependencies
Install Handsontable and the React wrapper in your project root:
Terminal window pnpm add handsontable@0.0.0-next-deba76c-20260408 @handsontable/react-wrapper@0.0.0-next-deba76c-20260408If you are creating a new Base setup, install Base Web and Styletron as well:
Terminal window pnpm add baseui styletron-engine-atomic styletron-reactRegister Handsontable modules
Register Handsontable modules once before rendering the grid.
import { registerAllModules } from 'handsontable/registry';import { registerTheme } from 'handsontable/themes';registerAllModules();Map Base design tokens to Handsontable colors
Create a colors object that maps Handsontable color keys to your Base CSS variables.
File:
src/theme/colorsBase.ts/*** Maps Handsontable theme colors to Base Web design tokens.* Keep this object focused on color variables so you can swap brands by changing token values only.*/export const colorsBase = {palette: {50: 'var(--bds-background-tertiary)',100: 'var(--bds-background-secondary)',200: 'var(--bds-background-primary)',300: 'var(--bds-border-opaque)',400: 'var(--bds-content-tertiary)',500: 'var(--bds-content-secondary)',600: 'var(--bds-content-primary)',700: 'var(--bds-content-inverse-secondary)',800: 'var(--bds-content-inverse-primary)',900: 'var(--bds-background-inverse-primary)',950: 'var(--bds-background-inverse-secondary)',},primary: {100: 'var(--bds-primary-a)',200: 'var(--bds-primary-b)',300: 'var(--bds-primary)',400: 'var(--bds-primary-hover)',500: 'var(--bds-primary-active)',600: 'var(--bds-content-primary)',},white: 'var(--bds-background-primary)',black: 'var(--bds-content-primary)',transparent: 'transparent',};If your app does not yet expose token variables, define them in your global stylesheet and keep names consistent with your design system naming convention.
Define icons and tokens
You can reuse Handsontable’s built-in icon and token sets, then override only what you need.
import iconsMain from 'handsontable/themes/static/variables/icons/main';import tokensHorizon from 'handsontable/themes/static/variables/tokens/horizon';For most Base Web integrations, start with the built-ins and only override shape and spacing tokens:
const baseThemeTokenOverrides = {wrapperBorderRadius: 'var(--bds-radius-200)',buttonBorderRadius: 'var(--bds-radius-200)',menuBorderRadius: 'var(--bds-radius-300)',inputBorderRadius: 'var(--bds-radius-200)',};Register and use the Base theme
Create and register your theme, then pass it to
HotTable.import React from 'react';import { HotTable } from '@handsontable/react-wrapper';import { registerTheme } from 'handsontable/themes';import { registerAllModules } from 'handsontable/registry';import iconsMain from 'handsontable/themes/static/variables/icons/main';import tokensHorizon from 'handsontable/themes/static/variables/tokens/horizon';import { colorsBase } from './theme/colorsBase';registerAllModules();const baseDataGridTheme = registerTheme('base-data-grid', {icons: iconsMain,colors: colorsBase,tokens: tokensHorizon,}).params({tokens: {wrapperBorderRadius: 'var(--bds-radius-200)',buttonBorderRadius: 'var(--bds-radius-200)',menuBorderRadius: 'var(--bds-radius-300)',inputBorderRadius: 'var(--bds-radius-200)',},});const data = [{ name: 'Alice', role: 'Designer', location: 'New York', active: true },{ name: 'Jon', role: 'Engineer', location: 'Berlin', active: false },{ name: 'Lina', role: 'Product Manager', location: 'Warsaw', active: true },];export default function BaseThemeExample() {return (<HotTabletheme={baseDataGridTheme}data={data}width="100%"height="auto"rowHeaders={true}colHeaders={['Name', 'Role', 'Location', 'Active']}columns={[{ data: 'name' },{ data: 'role' },{ data: 'location' },{ data: 'active', type: 'checkbox' },]}licenseKey="non-commercial-and-evaluation"/>);}Keep styles consistent with Base Web
When you tune the grid, follow this order:
- Update Base token values in one place.
- Reuse those tokens through your
colorsBaseobject. - Override only the minimum Handsontable tokens with
.params({ tokens: ... }).
This keeps the grid aligned with system-level style updates and avoids one-off CSS overrides.
Troubleshooting
Theme does not apply.
- Confirm the registered theme object is passed to
theme, not a string class name. - Make sure
registerAllModules()runs before renderingHotTable. - Check that your CSS variables are available on the grid container.
Colors look incorrect in dark mode.
- Verify your app switches the same token variables that
colorsBaseuses. - Avoid hardcoded hex values in
colorsBasewhen your app relies on dynamic themes.
Related
- Themes - Built-in themes and Theme API.
- Theme customization - Theme API parameters and CSS variable reference.
- Design system (Figma) - Figma kit and design tokens.
What you learned
You registered a custom Handsontable theme that maps Base Web design tokens to Handsontable colors and tokens. You used registerTheme with a colors object backed by var(--bds-*) CSS variables, Horizon tokens as the base, and .params() overrides to match Base Web’s border radius.
Next steps
- Handsontable with shadcn/ui - The same pattern using shadcn/ui CSS variables and Lucide icons.
- Handsontable with MUI - The same pattern reading colors from the MUI
Themeobject viauseTheme(). - Theme customization - Full reference for Theme API parameters and CSS variables.