7# Creating Documentation Code Examples
8
9This skill covers how to write runnable code examples that are embedded in documentation guide pages.
10
11## File Structure
12
13Each guide has framework-specific subdirectories for its examples:
14
15```
16docs/content/guides/category/feature/
17 feature.md # The guide page
18 javascript/ # JS examples
19 example1.js # Generated from TS - do not edit directly
20 example1.ts # Primary source - edit this first
21 react/ # React variants
22 example1.jsx # Generated from TSX
23 example1.tsx # Primary source
24 angular/ # Angular variants
25 example1.ts
26 example1.html # Template file
27 vue/ # Vue 3 variants
28 example1.vue # TypeScript SFC (<script setup lang="ts">)
29```
30
31## Key Rules
32
33- **25-60 lines per example.** Keep examples focused and scannable.
34- **One concept per example.** Use progressive numbering for complexity: example1 = basic setup, example2 = a configuration variation, example3 = advanced usage.
35- **TypeScript is primary.** Always write the .ts / .tsx file first for JavaScript and React examples. From docs/, generate the JS variant with: npm run docs:code-examples:generate-js -- <path-to-ts-file> (path relative to docs/). Never hand-edit generated JS files. For Vue, write TypeScript inside the .vue file with <script setup lang="ts"> — there is no separate JS variant to generate.
36- **Use realistic data.** Prefer createSpreadsheetData() or domain-appropriate sample data (product names, dates, currencies). Avoid trivial arrays like [1, 2, 3].
37
38## Required Elements in Every Example
39
401. **Imports** - use the base import and explicit registration:
41 ```js
42 import Handsontable from 'handsontable/base';
43 import { registerAllModules } from 'handsontable/registry';
44 registerAllModules();
45 ```
46 For examples that demonstrate tree-shaking, import individual plugins and cell types instead of registerAllModules().
47
482. **License key** - always include:
49 ```js
50 licenseKey: 'non-commercial-and-evaluation'
51 ```
52
533. **Container element** - target the conventional #example div:
54 ```js
55 const container = document.querySelector('#example');
56 ```
57
58## Framework-Specific Patterns
59
60**React (TSX/JSX):**
61```tsx
62import { HotTable } from '@handsontable/react-wrapper';
63import { registerAllModules } from 'handsontable/registry';
64registerAllModules();
65
66const App = () => {
67 return <HotTable data={data} licenseKey="non-commercial-and-evaluation" />;
68};
69```
70
71**Angular:**
72- All components must be **standalone** (standalone: true, imports: [HotTableModule]).
73- Use app.config.ts (not app.module.ts) with ApplicationConfig, provideZoneChangeDetection({ eventCoalescing: true }), and global HOT_GLOBAL_CONFIG for the license key.
74- Do **not** add licenseKey to individual <hot-table> bindings -- it is set globally in app.config.ts.
75- Template control flow: use @if / @for (x of list; track x.id) -- never *ngIf / *ngFor.
76- Name the component class AppComponent in every example.
77
78**Critical Angular JIT restrictions** — the docs site bootstraps Angular examples with JIT in the browser. JIT cannot load external files at runtime:
79
80- ❌ **Never use styleUrls** in standalone components. CSS is injected globally by the example-runner via the --css slot. If you need component-scoped styles, use inline styles: ['...'].
81- ❌ **Never use templateUrl**. Always define the component's template inline with template: \...\`. The angular/example1.html` file is the **outer wrapper** (selector tag) consumed by the example-runner -- it is not the component's template.
82- ❌ **Never inject services via the constructor**. Use inject() instead. JIT mode lacks TypeScript decorator metadata, so constructor DI throws NG0202.
83- ❌ **Never bind Handsontable hooks in the template** ((afterInit)="handler()"). Put hook functions inside gridSettings instead.
84- ❌ **Only import symbols you actually use**. Unused imports (e.g., RowObject, ViewChild, NgFor) can cause module resolution errors.
85
86The .ts file contains both app.component.ts and app.config.ts as separate /* file: ... */ sections within a single file:
87
88```typescript
89/* file: app.component.ts */
90import { Component } from '@angular/core';
91import { GridSettings, HotTableModule } from '@handsontable/angular-wrapper';
92
93@Component({
94 standalone: true,
95 imports: [HotTableModule],
96 selector: 'example1-feature-name',
97 template: `
98 <div>
99 <hot-table [data]="data" [settings]="gridSettings"></hot-table>
100 </div>
101 `,
102})
103export class AppComponent {
104 readonly data = [...];
105 readonly gridSettings: GridSettings = { ... };
106}
107/* end-file */
108
109/* file: app.config.ts */
110import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
111import { registerAllModules } from 'handsontable/registry';
112import { HOT_GLOBAL_CONFIG, HotGlobalConfig, NON_COMMERCIAL_LICENSE } from '@handsontable/angular-wrapper';
113
114registerAllModules();
115
116export const appConfig: ApplicationConfig = {
117 providers: [
118 provideZoneChangeDetection({ eventCoalescing: true }),
119 { provide: HOT_GLOBAL_CONFIG, useValue: { license: NON_COMMERCIAL_LICENSE } as HotGlobalConfig },
120 ],
121};
122/* end-file */
123```
124
125The angular/example1.html file is the outer wrapper (not the component template):
126```html
127<div>
128 <example1-feature-name></example1-feature-name>
129</div>
130```
131
132**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.
133
134See skill angular-wrapper-dev for the full reference.
135
136**Vue 3:**
137
138Write 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).
139
140- ✅ **Do:** one exampleN.vue file per example, with <script setup lang="ts">.
141- ❌ **Do not:** use plain <script setup> without lang="ts".
142- ❌ **Do not:** split logic into exampleN.js + exampleN.html (legacy pattern). When you touch an old split example, migrate it to a .vue SFC.
143- ❌ **Do not:** use the Options API (defineComponent with data(), methods, etc.) in new examples.
144
145**SFC skeleton:**
146
147```vue
148<script setup lang="ts">
149import { ref } from 'vue';
150import { HotTable } from '@handsontable/vue3';
151import { registerAllModules } from 'handsontable/registry';
152import type { GridSettings } from 'handsontable/settings';
153
154registerAllModules();
155
156const hotSettings = ref<GridSettings>({
157 data: [
158 ['Acme Corp', 'Q1 2025', '$4.2M'],
159 ['Vertex Industries', 'Q1 2025', '$18.7M'],
160 ],
161 colHeaders: true,
162 height: 'auto',
163 licenseKey: 'non-commercial-and-evaluation',
164});
165</script>
166
167<template>
168 <div id="example1">
169 <HotTable :settings="hotSettings" />
170 </div>
171</template>
172```
173
174**Vue-specific rules:**
175
176- Always use <script setup lang="ts">. Type grid options with GridSettings from handsontable/settings. Add local type aliases for row or domain data when the example uses object rows.
177- Call registerAllModules() once at the top level of <script setup> (not inside onMounted).
178- Import HotTable and HotColumn from @handsontable/vue3. Register them by using them in <template> (no global app.component() registration).
179- Put licenseKey: 'non-commercial-and-evaluation' inside the settings object passed to HotTable.
180- The root <div> in <template> must use an id that matches the example container in the guide (#example1 in ::: example #example1 :vue3).
181- Prefer a single :settings object for grid options. Use individual props only when the guide text highlights a specific prop.
182- Put Handsontable hooks (afterChange, beforeDataProviderFetch, etc.) inside the settings object, not as Vue event listeners on <HotTable>.
183- Use ref() from vue for reactive state that the template or handlers update (hotSettings, toggles, selected values). Use a plain const for hotSettings when reactive deep updates would trigger unwanted updateSettings() calls (for example, when only a status label changes beside the grid).
184- Always use useTemplateRef('refName') for template refs bound via ref="..." in <template>. Never use ref() for template refs.
185- HotTable instance access:
186
187```vue
188import { useTemplateRef } from 'vue';
189
190const hotRef = useTemplateRef<InstanceType<typeof HotTable>>('hotRef');
191```
192
193```vue
194<HotTable ref="hotRef" :settings="hotSettings" />
195```
196
197Access: hotRef.value?.hotInstance.
198
199- DOM element refs:
200
201```vue
202const dropdownRef = useTemplateRef<HTMLDivElement>('dropdownRef');
203```
204
205```vue
206<div ref="dropdownRef" class="theme-dropdown">...</div>
207```
208
209Access: dropdownRef.value. The string passed to useTemplateRef(...) must match the template ref attribute exactly.
210- For HotColumn, nest it inside <HotTable> in <template> and pass column options via :settings on each HotColumn.
211- Optional <style scoped> is allowed for example-only UI (buttons, status text). Example-runner CSS from the guide's --css slot still applies globally.
212
213**Presets** on the ::: example directive select dependencies: :vue3 (default), :vue3-languages, :vue3-vuex. Match the preset to the feature the page demonstrates.
214
215**Embedding a Vue SFC** (single tab, no --html / --js):
216
217```markdown
218::: example #example1 :vue3
219
220@[code](@/content/guides/category/feature/vue/example1.vue)
221
222:::
223```
224
225See skill vue-wrapper-dev for wrapper behavior (HotTable, HotColumn, settings propagation).
226
227## Embedding in the Guide
228
229After 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.
230
231## Checklist
232
233- [ ] TypeScript source written and tested (.ts/.tsx, or Vue .vue with lang="ts").
234- [ ] JS variant generated for JavaScript/React examples (not hand-written). Vue examples have no JS variant.
235- [ ] licenseKey: 'non-commercial-and-evaluation' present.
236- [ ] Imports use handsontable/base + registration pattern.
237- [ ] Example stays within 25-60 lines.
238- [ ] One concept per example with realistic data.
239- [ ] Vue examples use .vue SFC with <script setup lang="ts"> and Composition API (no split .js/.html, no Options API).
240