Hooking Architecture

Handoff is designed to be highly extensible through its powerful hooking architecture. Learn how to tap into the build pipeline at various stages to customize token transformations, generate custom documentation, or integrate with external tools. Empower your team to build a bespoke design system workflow that fits your unique needs.

Handoff's power lies in its extensibility. The hooking architecture allows you to intercept the design-to-code pipeline at critical points—whether you need to modify how tokens are transformed, customize the build process for your components, or run automated validations against your design system.

All hooks are configured within your handoff.config.js file.

Build Pipeline Transformers

The pipeline.transformers hook is the most powerful way to "alter the data" produced by Handoff. It allows you to add custom logic that runs after design tokens are extracted from Figma but before they are saved to disk.

Input

A DocumentationObject containing all extracted localStyles (colors, typography, effects) and components.

Example: Custom JSON Export

Generating a flat JSON file specifically for a mobile application or a secondary tool.

example.javascript
javascript
1// handoff.config.js 2module.exports = { 3 pipeline: { 4 transformers: [ 5 { 6 transformer: (options) => ({ 7 name: 'MobileTokenTransformer', 8 transform: (docs) => { 9 const tokens = { 10 colors: docs.localStyles.colors.map(c => ({ name: c.name, hex: c.value })), 11 }; 12 return { 13 design: { 14 'mobile-tokens.json': JSON.stringify(tokens, null, 2) 15 } 16 }; 17 } 18 }), 19 outDir: 'mobile', 20 format: 'json' 21 } 22 ] 23 } 24};

Build Configuration Hooks

These hooks allow you to intercept and modify the underlying build configurations for Vite and esbuild.

jsBuildConfig

Overrides the Vite configuration used for the main JavaScript bundle and component scripts.

  • Input: InlineConfig (Vite)
  • Returns: InlineConfig
example.javascript
javascript
1jsBuildConfig: (config) => { 2 config.define = { ...config.define, 'process.env.MY_VAR': '"value"' }; 3 return config; 4}

cssBuildConfig

Overrides the Vite configuration used for Sass/CSS compilation.

  • Input: InlineConfig (Vite)
  • Returns: InlineConfig
example.javascript
javascript
1cssBuildConfig: (config) => { 2 // Add custom PostCSS plugins or Sass options 3 return config; 4}

ssrBuildConfig

Overrides the esbuild configuration used to render React components on the server for documentation previews.

  • Input: BuildOptions (esbuild)
  • Returns: BuildOptions
example.javascript
javascript
1ssrBuildConfig: (config) => { 2 config.external = ['some-heavy-library']; 3 return config; 4}

Schema & Property Hooks

These hooks control how Handoff interprets component metadata and transitions between different data formats.

getSchemaFromExports

Defines where Handoff should find the property schema in a component's JavaScript exports.

  • Input: exports (The module exports object)
  • Returns: The schema object
example.javascript
javascript
1getSchemaFromExports: (exports) => { 2 return exports.myCustomSchema || exports.default; 3}

schemaToProperties

Transforms a raw schema object into Handoff's internal SlotMetadata format. Use this to support custom documentation formats or legacy schemas.

  • Input: schema (The object returned by getSchemaFromExports)
  • Returns: { [propertyName: string]: SlotMetadata }
example.javascript
javascript
1schemaToProperties: (schema) => { 2 // Map your custom schema format to Handoff properties 3 return Object.entries(schema).reduce((acc, [key, val]) => ({ 4 ...acc, 5 [key]: { type: 'text', name: key, default: val.default } 6 }), {}); 7}

Validation Hooks

validateComponent

An asynchronous hook that runs after a component is built. Use this to enforce design system rules or accessibility standards.

  • Input: component (TransformComponentTokensResult)
  • Returns: Promise<Record<string, ValidationResult>>
example.javascript
javascript
1validateComponent: async (component) => { 2 const results = {}; 3 4 // Check for minimum property requirements 5 if (!component.properties.label) { 6 results.metadata = { 7 description: 'Component Metadata Check', 8 passed: false, 9 messages: ['All buttons must have a label property.'] 10 }; 11 } 12 13 return results; 14}

Validated results are saved directly into the components.json and are displayed within the Handoff documentation UI.