Component definition
The component.json reference.
A component's definition, component.json, describes it to the paywall designer, the Voidhash
agent and publishing. #[component] builds the same definition into your module. Publishing checks
that both agree on its traits, props, events, preview states, capabilities and size. The id,
version, title, description, icon, category and keywords are the bundle's own.
{
"id": "acme/plan-picker",
"version": "1.0.0",
"title": "Plan picker",
"description": "Selectable plans with a purchase button.",
"icon": { "kind": "builtin", "name": "list" },
"category": "Pricing",
"keywords": ["plans", "pricing"],
"implementation": { "kind": "wasm", "abi": 2 },
"traits": { "frame": { "resize": "width" }, "appearance": {}, "effects": {} },
"children": { "kind": "none" },
"props": [
{ "name": "plans", "type": { "kind": "list", "of": { "kind": "product" } }, "default": [] },
{ "name": "accent", "title": "Accent", "type": { "kind": "color" }, "default": "#7c3aed" }
],
"events": [
{ "name": "selected", "payload": [{ "name": "productId", "type": { "kind": "string" } }] }
],
"previewStates": ["default"],
"capabilities": ["commerce"],
"panel": false,
"fallback": "placeholder",
"defaultSize": { "width": "fill", "height": "hug" }
}A bundle you publish to your organization needs a
component.json. Components in your project's .voidhash working copy use the definition
#[component] builds instead.
Fields
| Field | Meaning |
|---|---|
id | <namespace>/<name>. The namespace is your organization's; the name uses lowercase letters, digits and dashes. |
version | A semantic version such as 1.2.0. Each published version is permanent, so bump it for every publish. |
title | The name the designer shows. |
description | One or two sentences about what the component does. |
icon | { "kind": "builtin", "name": "list" } (such as component, list, timer, gift), or ship an icon.png. |
category | The group the designer lists the component in, such as Pricing. |
keywords | Words people search for in the designer. |
implementation | { "kind": "wasm", "abi": 2 } for components built with voidhash-ui. |
traits | The designer sections and canvas behavior the component takes part in. See Traits. |
children | { "kind": "none" }, { "kind": "any" }, or { "kind": "slots", "slots": [{ "name": "default" }] }. |
props | The settings the designer shows. See Props. |
events | What the component emits, such as a selection, that paywalls bind to actions. |
previewStates | The states the designer can preview the component in. The first is the default. |
capabilities | What the component can do for the person using your app. See Capabilities. |
panel | Whether the component ships its own editor panel. Without one, the designer shows its props. |
fallback | What an app that cannot run the component shows instead: placeholder, hide or children. |
defaultSize | The size a new instance starts at: fill, hug or a number of points, per axis. |
Traits
A trait gives every instance a designer section and its canvas behavior, as built-in layers have them. An instance keeps only the settings of the traits its definition declares.
| Trait | Designer section | What instances get |
|---|---|---|
frame | Position | Position, size, margins and how the instance sits in its parent. |
layout | Layout | Padding, gap, alignment and clipping of what the component holds. |
appearance | Appearance | Opacity, blend mode, visibility and corner radius. |
fill | Fill | Backgrounds: colors, gradients and image fills. |
border | Border | A border. |
outline | Outline | An outline. |
effects | Effects | Shadows, blurs, rotation and flips. |
states | States | States that restyle the instance when their condition holds. |
interactions | Interactions | Tap actions on the instance itself. |
variables | Variables | Variables declared on the instance. |
sharedElement | Shared element | A shared element id, so the instance flies between screens. |
Text layers also have typography, but component instances have no text style, so declaring it
gives them nothing. Expose text settings as props instead. An
editor panel edits only the style of the declared traits.
Resizing
frame takes resize, the canvas's resize handles: both, width or height for one axis, or
none to keep the instance's size. Add aspect: "intrinsic" to keep its proportions while
resizing. A component without frame sizes itself from defaultSize and can't be resized on the
canvas. In Rust, declare traits on the component:
#[component(traits(frame(resize = "width"), appearance, effects), size(width = "fill", height = "hug"))]Slots
A component with children: { "kind": "slots" } shows its inner layers where its code renders
slot() (the default slot) or slot_named("footer"). In the designer, each inner layer names
its slot.
Props
Each prop has a name, a type and a default. The type sets what the prop holds and the
control the designer shows; other fields label, group and localize it. See
Component props for every field, type and its Rust form.
The definition must declare what the component's code declares: the same traits, props, events, preview states, capabilities, panel and default size. Publishing names every difference it finds.
Older app versions
People keep using older versions of your app after you deploy a paywall. Voidhash sends a paywall only to an app whose SDK can run every component in it. An older SDK gets the answer for a location with nothing assigned, so it shows your own fallback instead of a broken paywall. Update the SDK in your app to reach it.
The same check applies to A/B tests. An app that can't show its assigned arm is shown nothing at that location and isn't counted as exposed.
Some components run natively inside the SDK. A recent SDK without such a component still gets the
paywall and shows the component's fallback in its place, so choose placeholder, hide or
children with that in mind.
Next steps
Something wrong or missing on this page? Tell us by email or on Discord.