Component API

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.


Component Metadata

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.

FieldTypeDescription
idstringA unique, filesystem-safe identifier for the component (e.g., hero_split).
titlestringThe human-readable name of the component (e.g., Hero Split).
descriptionstringA concise explanation of the component's purpose and usage.
typestringThe structural type of the component (e.g., block, element, combo).
groupstringA high-level category for organization (e.g., Combos, Forms, Navigation).
imagestringPath to a static preview image for the documentation gallery.
figmastringA direct link to the component or variant in your Figma design system.
categoriesstring[]Keywords for filtering and search (e.g., ['design', 'marketing']).
tagsstring[]Additional labels for granular discovery.
should_dostring[]Best practices for implementers and designers.
should_not_dostring[]Common pitfalls and anti-patterns to avoid.

Properties Architecture

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.

Supported Property Types

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).

Validation Rules

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.

Previews and Mock Data

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.javascript
javascript
1"previews": { 2 "my_preview_id": { 3 "title": "Human Readable Title", 4 "values": { 5 "property_name": "data_value" 6 } 7 } 8}

Component Examples

Simple Example: Basic Button

A minimal configuration mapping only a label and a theme toggle.

example.json
json
1{ 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}

Complex Example: Hero Split

A production-ready component with nested arrays, imagery, and video embedding.

example.js
js
1/** @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};

Entry Points

The entries object defines which files comprise the component implementation.

example.json
json
1"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}