| name | add-component |
| description | Create or edit Flint Static UI components (TypeScript classes extending Component<T>). Use when building reusable server-rendered UI that appears across multiple pages via {{tag}} placeholders in templates. |
Add / Edit a Component
Create or modify reusable UI components in src/components/. Components are TypeScript classes that return HTML strings, invoked via {{tag}} placeholders in templates.
Trigger Phrases
- "Create a [name] component"
- "Add a reusable [card / banner / alert / grid] UI element"
- "Build a [name] widget that appears on multiple pages"
- "I need a {{tag}} that renders [description]"
- "Add a [testimonial / pricing / FAQ] section component"
- "Make the [name] component show [new data]"
- "Extend the [existing component] to support [variant]"
When to Use
- Building reusable UI that appears on multiple pages (nav, footer, card, alert)
- Rendering logic needs loops, conditionals, or computed values
- HTML output needs
escapeHtml() for safety
- UI pattern needs unit testing
When NOT to Use
- Page-specific content → use Markdown in
content/
- Page-specific HTML → use
:::html block in content
- Page layout structure → use a template
- Client-side interactivity → use
src/client/*.ts
Procedure
1. Write tests first
Create src/components/<name>.test.ts:
import { describe, it, expect } from 'bun:test';
import { MyComponent } from './<name>.js';
describe('MyComponent', () => {
it('should render with required props', () => {
const html = MyComponent.render({ });
expect(html).toContain('<expected-element');
});
it('should escape user content', () => {
const html = MyComponent.render({
text: '<script>alert("xss")</script>',
});
expect(html).not.toContain('<script>');
expect(html).toContain('<script>');
});
});
2. Implement the component
Create src/components/<name>.ts:
import { Component, type ComponentProps } from './component.js';
export interface MyComponentProps extends ComponentProps {
}
export class MyComponent extends Component<MyComponentProps> {
render(): string {
return `<div class="...">
${this.escapeHtml(this.props.someField)}
</div>`;
}
}
Rules:
- Extend
Component<T> with a typed props interface
render() returns pure HTML string — no side effects
this.escapeHtml() on every user-supplied string
this.classNames() for conditional CSS classes
- Tailwind utility classes only — no
<style> tags
3. Export tagDefs from the component file
At the bottom of src/components/<name>.ts, add:
import type { TagDef } from '../templates/tag-registry.js';
export const tagDefs: TagDef[] = [
{
tag: 'my-tag',
label: 'My Tag',
icon: '🔧',
description: 'One-line description of this component.',
resolve: (ctx) => {
const data = ctx.frontmatter['MyData'] as MyProps | undefined;
if (!data) return '';
return MyComponent.render(data);
},
},
];
- The tag registry auto-discovers
tagDefs exports by scanning src/components/ — do NOT edit tag-engine.ts
- Return
'' when frontmatter data is missing so {{#if my-tag}} suppresses the block
- Never hardcode props — content files drive data into components via frontmatter
label, icon, description power the manager's component browser
4. Extend TemplateContext if needed
If the component needs data not in TemplateContext (src/templates/template-registry.ts), add the field and populate it in src/core/builder.ts. Most components should read from ctx.frontmatter directly — only add TemplateContext fields for site-wide data (navigation, siteLabels).
5. Add component data to content frontmatter
The content file should provide structured YAML matching the component's props:
---
MyData:
fieldA: value
fieldB: value
---
The tagDefs resolve function reads this from ctx.frontmatter and passes it to the component. Content authors control the data; the component controls the presentation.
5. Use in templates
Add {{my-tag}} to themes/default/templates/*.html (or the active theme's template). Guard optional tags:
{{#if my-tag}}{{my-tag}}{{/if}}
6. Run tests and build
bun run test:run
bun run build
Checklist
References
references/base-class.md — Component<T> API and inherited methods
references/tag-registration.md — How to register tags in the engine
references/built-in-components.md — All existing components for reference