Components
EmberKit components are plain functions that return JSX elements. They follow the same mental model as React but with zero runtime overhead by default.
Defining a Component
function Button({ children, variant = 'primary' }: { children: unknown; variant?: string }) {
return (
<button className={`btn btn-${variant}`}>
{children}
</button>
);
}
Components receive a single props object and return JSX. There are no class components, no lifecycle methods, and no this binding.
Using Components
function App() {
return (
<div>
<Button>Click me</Button>
<Button variant="secondary">Cancel</Button>
</div>
);
}
Children
Components can accept children to act as wrappers:
function Card({ children, title }: { children: unknown; title: string }) {
return (
<div className="card">
<h3 className="card-title">{title}</h3>
<div className="card-body">{children}</div>
</div>
);
}
Conditional Rendering
Use JavaScript expressions for conditional output:
function Status({ online }: { online: boolean }) {
return (
<div>
{online ? <span className="badge-green">Online</span> : <span className="badge-red">Offline</span>}
</div>
);
}
Lists
Use map to render lists with key props:
function TodoList({ items }: { items: string[] }) {
return (
<ul>
{items.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
);
}
Static vs Interactive
Key Concept
Static — no handlers, no data-ek-bind: pure HTML, no client JS.
Interactive — onClick / data-ek-bind: targeted hydration after SSR.
function Title() {
return <h1>Hello</h1>;
}
function Counter() {
const [count, setCount] = createSignal(0);
return (
<div>
<button type="button" onClick={() => setCount((c) => c + 1)}>+</button>
<span data-ek-bind={count}>{count()}</span>
</div>
);
}
Details: Hydration. Signal props on components: Signals.
Props Interface
Define prop types with TypeScript interfaces:
interface ButtonProps {
children: unknown;
variant?: 'primary' | 'secondary' | 'ghost';
size?: 'sm' | 'md' | 'lg';
disabled?: boolean;
onClick?: () => void;
}
function Button({ children, variant = 'primary', size = 'md', disabled, onClick }: ButtonProps) {
return (
<button
className={`btn btn-${variant} btn-${size}`}
disabled={disabled}
onClick={onClick}
>
{children}
</button>
);
}
Next Steps
- Routing - File-based routing
- Signals - Reactive state
- SSR & SSG - Rendering modes
- Context - Shared state across components
- Internationalization - JSON translation files and locale routing