Explore the complete specification for Handoff's Component API. This guide details every metadata field, property type, and configuration option available to help you build structured, versioned, and machine-readable components. Master the properties architecture to create a flexible and robust design system interface.
The Handoff Component API is the architectural foundation for how components are defined, discovered, and rendered. By following a standardized configuration schema, you ensure that your design system is as understandable to machines as it is to humans.
Every component is defined by a config.json or config.js file. The following top-level fields allow you to categorize and document your components.
| Field | Type | Description |
|---|---|---|
id | string | A unique, filesystem-safe identifier for the component (e.g., hero_split). |
title | string | The human-readable name of the component (e.g., Hero Split). |
description | string | A concise explanation of the component's purpose and usage. |
type | string | The structural type of the component (e.g., block, element, combo). |
group | string | A high-level category for organization (e.g., Combos, Forms, Navigation). |
image | string | Path to a static preview image for the documentation gallery. |
figma | string | A direct link to the component or variant in your Figma design system. |
categories | string[] | Keywords for filtering and search (e.g., ['design', 'marketing']). |
tags | string[] | Additional labels for granular discovery. |
should_do | string[] | Best practices for implementers and designers. |
should_not_do | string[] | Common pitfalls and anti-patterns to avoid. |
The properties object is where you define the interface of your component. Handoff uses this schema to generate documentation and provide a structured data model for your templates.
Each property must define a type. Handoff supports several primitive and complex types:
text: Standard string input for labels, titles, or body copy.boolean: A toggle for features like "Dark Mode" or optional icons.array: A list of items (e.g., segments in a breadcrumb or items in a list).object: A nested group of related properties.link: A combined object containing text, href, and target.button: An enhanced link object with support for label, url, and metadata like rel.image: An object containing src and alt.video_file: A link to a raw video asset.video_embed: Support for embedded video players (e.g., YouTube/Vimeo).You can enforce data integrity using the rules object within each property definition:
required: (boolean) Whether the field is mandatory.content: (min/max) Enforce character count limits for text fields.pattern: (regex) Validate input against a specific regular expression.dimensions: (min/max/recommend) Enforce size requirements for images and videos.filesize: (number) Limit the maximum allowed size for uploaded assets.The previews object allows you to define multiple use cases or "stories" for your component. Each preview provides a values map that populates the component's properties.
example.javascriptjavascript1"previews": { 2 "my_preview_id": { 3 "title": "Human Readable Title", 4 "values": { 5 "property_name": "data_value" 6 } 7 } 8}
A minimal configuration mapping only a label and a theme toggle.
example.jsonjson1{ 2 "title": "Simple Button", 3 "id": "simple_button", 4 "type": "element", 5 "properties": { 6 "label": { 7 "name": "Label", 8 "type": "text", 9 "default": "Click Me" 10 }, 11 "dark": { 12 "name": "Dark Theme", 13 "type": "boolean", 14 "default": false 15 } 16 } 17}
A production-ready component with nested arrays, imagery, and video embedding.
example.jsjs1/** @type {import('handoff-app').Component} */ 2module.exports = { 3 "title": "Hero Split", 4 "id": "hero_split", 5 "type": "block", 6 "group": "Combos", 7 "properties": { 8 "dark": { 9 "name": "Dark Theme", 10 "type": "boolean", 11 "default": false 12 }, 13 "title_prefix": { 14 "name": "Title Prefix", 15 "type": "text", 16 "rules": { "content": { "min": 5, "max": 15 } } 17 }, 18 "primary": { 19 "name": "Primary CTA", 20 "type": "button", 21 "default": { "label": "Get Started", "url": "/" } 22 }, 23 "video": { 24 "name": "Video", 25 "type": "video_embed" 26 }, 27 "breadcrumb": { 28 "name": "Breadcrumb", 29 "type": "array", 30 "items": { 31 "type": "object", 32 "properties": { 33 "link": { "type": "link" }, 34 "active": { "type": "boolean" } 35 } 36 } 37 } 38 }, 39 "previews": { 40 "standard": { 41 "title": "Standard Hero", 42 "values": { 43 "title_prefix": "Welcome", 44 "primary": { "label": "Start Now", "url": "/docs" } 45 } 46 } 47 } 48};
The entries object defines which files comprise the component implementation.
example.jsonjson1"entries": { 2 "scss": "./style.scss", // The component's styles 3 "js": "./script.js", // Optional client-side logic 4 "template": "./template.hbs" // The primary HTML template 5}