name: creating-docs-examples path: docs/** description: Use when creating code examples for documentation pages - JavaScript, TypeScript, React, Angular, and Vue variants with proper imports, registration, and license key
Creating Documentation Code Examples
This skill covers how to write runnable code examples that are embedded in documentation guide pages.
File Structure
Each guide has framework-specific subdirectories for its examples:
docs/content/guides/category/feature/
feature.md # The guide page
javascript/ # JS examples
example1.js # Generated from TS - do not edit directly
example1.ts # Primary source - edit this first
react/ # React variants
example1.jsx # Generated from TSX
example1.tsx # Primary source
angular/ # Angular variants
example1.ts
example1.html # Template file
vue/ # Vue 3 variants
example1.vue # TypeScript SFC (`<script setup lang="ts">`)
Key Rules
- 25-60 lines per example. Keep examples focused and scannable.
- One concept per example. Use progressive numbering for complexity:
example1= basic setup,example2= a configuration variation,example3= advanced usage. - TypeScript is primary. Always write the
.ts/.tsxfile first for JavaScript and React examples. Fromdocs/, generate the JS variant with:npm run docs:code-examples:generate-js -- <path-to-ts-file>(path relative todocs/). Never hand-edit generated JS files. For Vue, write TypeScript inside the.vuefile with<script setup lang="ts">— there is no separate JS variant to generate. - Use realistic data. Prefer
createSpreadsheetData()or domain-appropriate sample data (product names, dates, currencies). Avoid trivial arrays like[1, 2, 3].
Required Elements in Every Example
Imports - use the base import and explicit registration:
import Handsontable from 'handsontable/base'; import { registerAllModules } from 'handsontable/registry'; registerAllModules();For examples that demonstrate tree-shaking, import individual plugins and cell types instead of
registerAllModules().License key - always include:
licenseKey: 'non-commercial-and-evaluation'Container element - target the conventional
#examplediv:const container = document.querySelector('#example');
Framework-Specific Patterns
React (TSX/JSX):
import { HotTable } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
registerAllModules();
const App = () => {
return <HotTable data={data} licenseKey="non-commercial-and-evaluation" />;
};
Angular:
- All components must be standalone (
standalone: true,imports: [HotTableModule]). - Use
app.config.ts(notapp.module.ts) withApplicationConfig,provideZoneChangeDetection({ eventCoalescing: true }), and globalHOT_GLOBAL_CONFIGfor the license key. - Do not add
licenseKeyto individual<hot-table>bindings -- it is set globally inapp.config.ts. - Template control flow: use
@if/@for (x of list; track x.id)-- never*ngIf/*ngFor. - Name the component class
AppComponentin every example.
Critical Angular JIT restrictions — the docs site bootstraps Angular examples with JIT in the browser. JIT cannot load external files at runtime:
- ❌ Never use
styleUrlsin standalone components. CSS is injected globally by the example-runner via the--cssslot. If you need component-scoped styles, use inlinestyles: ['...']. - ❌ Never use
templateUrl. Always define the component's template inline withtemplate: \...`. Theangular/example1.html` file is the outer wrapper (selector tag) consumed by the example-runner -- it is not the component's template. - ❌ Never inject services via the constructor. Use
inject()instead. JIT mode lacks TypeScript decorator metadata, so constructor DI throwsNG0202. - ❌ Never bind Handsontable hooks in the template (
(afterInit)="handler()"). Put hook functions insidegridSettingsinstead. - ❌ Only import symbols you actually use. Unused imports (e.g.,
RowObject,ViewChild,NgFor) can cause module resolution errors.
The .ts file contains both app.component.ts and app.config.ts as separate /* file: ... */ sections within a single file:
/* file: app.component.ts */
import { Component } from '@angular/core';
import { GridSettings, HotTableModule } from '@handsontable/angular-wrapper';
@Component({
standalone: true,
imports: [HotTableModule],
selector: 'example1-feature-name',
template: `
<div>
<hot-table [data]="data" [settings]="gridSettings"></hot-table>
</div>
`,
})
export class AppComponent {
readonly data = [...];
readonly gridSettings: GridSettings = { ... };
}
/* end-file */
/* file: app.config.ts */
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
import { registerAllModules } from 'handsontable/registry';
import { HOT_GLOBAL_CONFIG, HotGlobalConfig, NON_COMMERCIAL_LICENSE } from '@handsontable/angular-wrapper';
registerAllModules();
export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({ eventCoalescing: true }),
{ provide: HOT_GLOBAL_CONFIG, useValue: { license: NON_COMMERCIAL_LICENSE } as HotGlobalConfig },
],
};
/* end-file */
The angular/example1.html file is the outer wrapper (not the component template):
<div>
<example1-feature-name></example1-feature-name>
</div>
Edit on StackBlitz: When you use Edit on StackBlitz, docs/public/example-tabs.js merges each framework's companion example*.html into the generated app shell. parseDocsExampleHtmlForStackBlitz uses the browser DOMParser to collect style nodes for <head> and drop script nodes from the body fragment. mergeCompanionHtmlForStackBlitz wires that into the StackBlitz template. Examples with no HTML tab keep the previous default mount markup.
See skill angular-wrapper-dev for the full reference.
Vue 3:
Write every new or updated Vue example as a TypeScript Single-File Component (.vue) using the Composition API and <script setup lang="ts">. The docs example-runner loads vue/example*.vue modules and mounts them with createApp() (see docs/src/scripts/example-runner.ts).
- ✅ Do: one
exampleN.vuefile per example, with<script setup lang="ts">. - ❌ Do not: use plain
<script setup>withoutlang="ts". - ❌ Do not: split logic into
exampleN.js+exampleN.html(legacy pattern). When you touch an old split example, migrate it to a.vueSFC. - ❌ Do not: use the Options API (
defineComponentwithdata(),methods, etc.) in new examples.
SFC skeleton:
<script setup lang="ts">
import { ref } from 'vue';
import { HotTable } from '@handsontable/vue3';
import { registerAllModules } from 'handsontable/registry';
import type { GridSettings } from 'handsontable/settings';
registerAllModules();
const hotSettings = ref<GridSettings>({
data: [
['Acme Corp', 'Q1 2025', '$4.2M'],
['Vertex Industries', 'Q1 2025', '$18.7M'],
],
colHeaders: true,
height: 'auto',
licenseKey: 'non-commercial-and-evaluation',
});
</script>
<template>
<div id="example1">
<HotTable :settings="hotSettings" />
</div>
</template>
Vue-specific rules:
- Always use
<script setup lang="ts">. Type grid options withGridSettingsfromhandsontable/settings. Add localtypealiases for row or domain data when the example uses object rows. - Call
registerAllModules()once at the top level of<script setup>(not insideonMounted). - Import
HotTableandHotColumnfrom@handsontable/vue3. Register them by using them in<template>(no globalapp.component()registration). - Put
licenseKey: 'non-commercial-and-evaluation'inside the settings object passed toHotTable. - The root
<div>in<template>must use anidthat matches the example container in the guide (#example1in::: example #example1 :vue3). - Prefer a single
:settingsobject for grid options. Use individual props only when the guide text highlights a specific prop. - Put Handsontable hooks (
afterChange,beforeDataProviderFetch, etc.) inside the settings object, not as Vue event listeners on<HotTable>. - Use
ref()fromvuefor reactive state that the template or handlers update (hotSettings, toggles, selected values). Use a plainconstforhotSettingswhen reactive deep updates would trigger unwantedupdateSettings()calls (for example, when only a status label changes beside the grid). - Always use
useTemplateRef('refName')for template refs bound viaref="..."in<template>. Never useref()for template refs. - HotTable instance access:
import { useTemplateRef } from 'vue';
const hotRef = useTemplateRef<InstanceType<typeof HotTable>>('hotRef');
<HotTable ref="hotRef" :settings="hotSettings" />
Access: hotRef.value?.hotInstance.
- DOM element refs:
const dropdownRef = useTemplateRef<HTMLDivElement>('dropdownRef');
<div ref="dropdownRef" class="theme-dropdown">...</div>
Access: dropdownRef.value. The string passed to useTemplateRef(...) must match the template ref attribute exactly.
- For
HotColumn, nest it inside<HotTable>in<template>and pass column options via:settingson eachHotColumn. - Optional
<style scoped>is allowed for example-only UI (buttons, status text). Example-runner CSS from the guide's--cssslot still applies globally.
Presets on the ::: example directive select dependencies: :vue3 (default), :vue3-languages, :vue3-vuex. Match the preset to the feature the page demonstrates.
Embedding a Vue SFC (single tab, no --html / --js):
::: example #example1 :vue3
@[code](@/content/guides/category/feature/vue/example1.vue)
:::
See skill vue-wrapper-dev for wrapper behavior (HotTable, HotColumn, settings propagation).
Embedding in the Guide
After creating example files, embed them in the guide's .md file using the @[code] directive inside an ::: example container. See the writing-docs-pages skill for the full embedding syntax.
Checklist
- TypeScript source written and tested (
.ts/.tsx, or Vue.vuewithlang="ts"). - JS variant generated for JavaScript/React examples (not hand-written). Vue examples have no JS variant.
-
licenseKey: 'non-commercial-and-evaluation'present. - Imports use
handsontable/base+ registration pattern. - Example stays within 25-60 lines.
- One concept per example with realistic data.
- Vue examples use
.vueSFC with<script setup lang="ts">and Composition API (no split.js/.html, no Options API).