A design system starts drifting long before anyone notices a broken component.
The first warning signs are usually quieter.
The website says a component supports React Native, but the native package does not export it.
Storybook has a story, but the component page is missing.
One checker assumes tests are required while another does not know the component is supposed to support keyboard interaction.
A generator creates a component, but someone still has to remember to update several registries by hand.
None of those failures starts inside the component implementation itself.
They start because the same facts are being maintained in too many places.
In Vellira, we address that with machine-readable component metadata: one canonical registry describing stable product and tooling facts about every component.
Generators, documentation tooling, completeness checks, and quality validation can then consume the same contract instead of rediscovering those facts independently.
The hidden cost of five component lists
Imagine maintaining separate lists for:
package exports
website catalog
Storybook expectations
documentation requirements
quality checks
With five components, that may be fine.
With twenty, every component starts carrying more context:
name
layer
category
platforms
status
capabilities
dependencies
required engineering surfaces
A component may be Web-only.
Another may support React and React Native.
A form control needs required and invalid states.
A navigation component needs keyboard behavior.
An overlay may require focus management and a portal.
Once those facts exist in several places, drift becomes almost inevitable.
Every change becomes several opportunities to forget something.
Automation helps.
But automation still needs an authority.
That is where metadata becomes useful.
What belongs in component metadata?
A simplified version of Vellira's component contract looks like this:
interface ComponentMetadata {
name: string;
layer:
| 'primitives'
| 'components'
| 'patterns';
category:
| 'action'
| 'form'
| 'navigation'
| 'overlay'
| 'feedback'
| 'data-display'
| 'layout'
| 'utility';
platforms: readonly (
| 'react'
| 'react-native'
)[];
profile:
| 'base'
| 'form-control'
| 'compound'
| 'overlay';
status:
| 'experimental'
| 'beta'
| 'stable'
| 'deprecated';
capabilities?: readonly ComponentCapability[];
dependencies?: ComponentDependencies;
requirements: ComponentRequirements;
}
Capabilities describe reusable facts such as:
controlled
uncontrolled
disabled
required
invalid
loading
keyboard
focus-management
compound-api
portal
responsive
Requirements describe which engineering surfaces a component must have:
- tests,
- Storybook,
- documentation,
- accessibility evidence,
- tokens,
- icons,
- component-specific token contracts.
The important part is what metadata does not contain.
It is not documentation prose.
It is not an implementation.
It is a compact collection of facts that tooling can make decisions from.
Different components should produce different contracts
A useful metadata model should represent real differences without creating a special schema for every component.
Consider a few examples.
Button
Button has a relatively small contract.
It supports React and React Native and has capabilities such as:
disabled
loading
It does not need the same behavioral model as an overlay or compound navigation component.
Simple components should be allowed to have simple metadata.
FormField
FormField belongs to a different architectural layer.
It is a pattern, belongs to the form category, and uses a form-control profile.
Its capabilities include things like:
disabled
required
invalid
compound-api
That tells tooling something important without forcing it to infer architecture from the name FormField.
Tabs
Tabs declares things like:
controlled
uncontrolled
keyboard
focus-management
compound-api
A checker should not need code like:
if component === "Tabs":
remember keyboard tests
The component declares the capability once.
Modal
An overlay can declare:
controlled
uncontrolled
keyboard
focus-management
compound-api
portal
That says focus management and portal behavior belong to the component contract.
It does not try to describe the exact focus algorithm.
That remains implementation work.
This distinction is important.
Metadata should be a contract, not just configuration
A TypeScript interface is not enough.
The values themselves need validation.
Otherwise something like:
focus-managment
can silently become a new accidental capability because somebody misspelled focus-management.
So Vellira validates things such as:
- supported platforms,
- supported layers,
- supported categories,
- supported profiles,
- lifecycle statuses,
- capability names,
- duplicate values,
- dependency structure,
- required engineering fields.
Invalid metadata fails before downstream tooling starts making assumptions from it.
That changes the mental model.
Metadata is not:
Here is some configuration that probably describes the component.
It is:
Here is the contract downstream tooling is allowed to trust.
One registry gives every tool the same starting point
Individual components can keep separate metadata files for reviewability.
But tooling needs one deterministic registry.
Conceptually:
Button.metadata.ts
Select.metadata.ts
Tabs.metadata.ts
Modal.metadata.ts
...
↓
componentMetadata registry
Consumers start from the same registry rather than independently scanning directories.
That gives us an important distinction:
Directory scanning tells you what happens to exist.
Metadata tells you what is supposed to exist.
Those are not the same question.
A half-generated component can leave files on disk.
A removed export can leave an implementation behind.
File presence alone is not enough to describe the product.
Generation should create the contract too
A source of truth is much more useful when adding a component does not require manually remembering to update it.
In Vellira, component generation includes metadata in the generation plan.
Conceptually:
component intent
↓
generation plan
↓
implementation
types
tests
stories
↓
Component.metadata.ts
↓
canonical registry
Registration needs to be deterministic too.
Repeated generation should not:
- duplicate entries,
- randomly reorder imports,
- create unrelated diffs,
- leave partially registered components.
That is why generator quality is not just about generating files.
It is about producing stable repository state.
Completeness checks become product-aware
This is where metadata starts paying for itself.
Instead of maintaining another list of known components, a completeness checker can read the canonical registry.
The metadata says:
this component supports React
this component supports React Native
tests are required
Storybook is required
docs are required
accessibility evidence is required
The checker then compares those promises with the repository.
Conceptually:
metadata
↓
what should exist?
↓
repository
↓
does it actually exist?
If React Native support is declared but the native implementation is missing, the result is not a reviewer eventually noticing it.
It becomes deterministic:
react-native declared
↓
native implementation expected
↓
implementation missing
↓
INCOMPLETE
The same pattern works for:
- public exports,
- tests,
- stories,
- docs,
- accessibility surfaces,
- tokens.
This removes a lot of memory work from review.
Quality checks can consume the same facts
Completeness and quality are different problems.
Completeness asks:
Are all required surfaces present?
Quality asks:
Does the implementation satisfy our engineering rules?
They should remain separate systems.
But they can share the same component facts.
A quality engine can receive:
component metadata
selected platform
repository root
and decide which checks are applicable.
A component that does not support React Native should not accidentally run native-specific checks.
A component declaring keyboard behavior can participate in keyboard-related validation without every rule maintaining its own list of component names.
That is exactly the kind of reuse metadata is good at.
Source of truth does not mean source of everything
This boundary matters a lot.
Metadata should not contain:
- every public prop,
- every event sequence,
- CSS,
- React Native styles,
- complete accessibility implementation,
- documentation prose,
- test cases,
- visual design decisions.
For example:
keyboard
in metadata means:
Keyboard behavior belongs to this component's contract.
It does not mean:
Keyboard behavior has been proven correct.
Tests and validation still need to provide that evidence.
Likewise:
docs: true
means documentation is required.
The documentation itself still belongs to the documentation system.
If metadata tries to describe every runtime detail, it eventually becomes another programming language.
At that point it stops reducing complexity and starts creating it.
Avoid component-name heuristics
This is fragile:
if component === "Tabs":
run keyboard checks
if component === "Modal":
expect portal behavior
if component === "FormField":
treat as pattern
Names are product identity.
They should not become the architecture of every tool.
A stronger model asks:
Does it declare keyboard behavior?
Does it declare portal behavior?
Which layer does it belong to?
Which profile does it use?
Which platforms does it support?
That means a future component can participate in existing tooling without adding another hardcoded condition.
This is one of the biggest benefits of a reusable capability vocabulary.
Metadata turns drift into evidence
Without a canonical contract, inconsistency usually appears as a review surprise.
Someone notices:
The website says this supports native, but the package doesn't.
Or:
We forgot Storybook for this component.
Or:
This overlay has no accessibility coverage.
With metadata-driven tooling, the repository can identify the disagreement itself.
The important transformation is:
human memory
↓
explicit contract
↓
deterministic comparison
↓
actionable finding
The tooling is not replacing engineering judgment.
It is removing the things humans should not have to remember manually.
Keep independent systems independent
Once metadata becomes useful, there is a temptation to make everything depend directly on it.
That can create another kind of coupling.
In Vellira, the responsibilities remain separate:
component metadata
→ canonical facts
generator
→ deterministic scaffold and registration
completeness checker
→ required surface presence
quality checker
→ engineering rule evaluation
docs / website / Storybook
→ actual content and presentation
They share facts.
They do not become one giant subsystem.
That separation lets each piece evolve without turning the metadata schema into the center of every implementation decision.
Practical rules I would use elsewhere
If I were introducing component metadata into another design system, I would follow a few rules.
1. Centralize facts that are already duplicated
Do not start by designing a universal ontology.
Start with facts several systems already maintain independently.
2. Use constrained vocabularies
This:
experimental | beta | stable | deprecated
is far more useful to automation than an unrestricted status string.
3. Validate before consumption
Malformed metadata should fail before downstream tooling acts on it.
4. Keep one canonical registry
Separate files are good for review.
One deterministic registry is good for tooling.
5. Let generators register metadata automatically
If humans still need to remember registry plumbing, there is still a memory gap.
6. Let requirements drive checks
A metadata field becomes useful when something can verify it.
7. Prefer reusable capabilities over component names
Model the behavior, not a growing list of exceptions.
8. Keep evidence outside the declaration
Metadata declares expectations.
Tests, docs, and implementation provide evidence.
9. Treat schema changes like API changes
Changing the vocabulary changes what tooling is allowed to assume.
10. Stop before metadata becomes another language
If your schema starts describing every runtime branch and style decision, it has gone too far.
A source of truth is useful because reality can disagree with it
That sounds contradictory, but it is the point.
Metadata creates a stable statement:
This component supports React and React Native.
Tests are required.
Storybook is required.
Documentation is required.
Accessibility evidence is required.
These capabilities belong to its contract.
Then the repository can be compared against those statements.
When implementation, docs, exports, tests, or stories disagree with the contract, the disagreement becomes detectable.
Without the canonical statement, every downstream system has to guess.
That is why component metadata can be much more than a catalog convenience.
It becomes a compact contract between a component library and the tooling responsible for keeping that library coherent.
For Vellira, the goal is not to describe everything.
It is to describe stable facts once, validate them, and stop asking every downstream system to rediscover them.
Vellira is an open-source React and React Native design system being built in public.
Top comments (0)