Exemples
Exemples concrets pour vous aider à construire avec EmberKit.
Compteur
Le compteur classique — démontre les signals et la gestion d'événements :
import { createSignal } from '@emberkit/core';
function Counter() {
const [count, setCount] = createSignal(0);
return (
<div>
<p>Count: {count()}</p>
<button onClick={() => setCount(c => c + 1)}>Increment</button>
<button onClick={() => setCount(c => c - 1)}>Decrement</button>
<button onClick={() => setCount(0)}>Reset</button>
</div>
);
}
Liste de tâches
Une app todo complète avec ajout, bascule et suppression :
import { createSignal, createMemo } from '@emberkit/core';
interface Todo {
id: number;
text: string;
done: boolean;
}
function TodoApp() {
const [todos, setTodos] = createSignal<Todo[]>([]);
const [input, setInput] = createSignal('');
const remaining = createMemo(() => todos().filter(t => !t.done).length);
function addTodo() {
if (!input()) return;
setTodos(prev => [
...prev,
{ id: Date.now(), text: input(), done: false },
]);
setInput('');
}
function toggleTodo(id: number) {
setTodos(prev =>
prev.map(t => t.id === id ? { ...t, done: !t.done } : t)
);
}
function removeTodo(id: number) {
setTodos(prev => prev.filter(t => t.id !== id));
}
return (
<div>
<h1>Todos ({remaining()} remaining)</h1>
<form onSubmit={(e) => { e.preventDefault(); addTodo(); }}>
<input
value={input()}
onInput={(e) => setInput(e.target.value)}
placeholder="What needs to be done?"
/>
<button type="submit">Add</button>
</form>
<ul>
{todos().map(todo => (
<li key={todo.id}>
<input
type="checkbox"
checked={todo.done}
onChange={() => toggleTodo(todo.id)}
/>
<span style={{ textDecoration: todo.done ? 'line-through' : 'none' }}>
{todo.text}
</span>
<button onClick={() => removeTodo(todo.id)}>Delete</button>
</li>
))}
</ul>
</div>
);
}
Bascule de thème
Changement de thème basé sur le context :
import { createContext, useContext, createSignal } from '@emberkit/core';
const ThemeContext = createContext<{ theme: string; toggle: () => void }>();
function App() {
const [theme, setTheme] = createSignal('light');
const toggle = () => setTheme(t => t === 'light' ? 'dark' : 'light');
return (
<ThemeContext.Provider value={{ theme: theme(), toggle }}>
<div className={theme()}>
<Header />
<main>Content</main>
</div>
</ThemeContext.Provider>
);
}
function Header() {
const { theme, toggle } = useContext(ThemeContext);
return (
<header>
<span>Theme: {theme}</span>
<button onClick={toggle}>Toggle</button>
</header>
);
}
Filtre de recherche
Filtrez une liste avec des valeurs calculées :
import { createSignal, createMemo } from '@emberkit/core';
const items = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry'];
function SearchFilter() {
const [query, setQuery] = createSignal('');
const filtered = createMemo(() =>
items.filter(item =>
item.toLowerCase().includes(query().toLowerCase())
)
);
return (
<div>
<input
value={query()}
onInput={(e) => setQuery(e.target.value)}
placeholder="Search fruits..."
/>
<ul>
{filtered().map(item => (
<li key={item}>{item}</li>
))}
</ul>
<p>{filtered().length} results</p>
</div>
);
}
Récupération de données
Récupérez et affichez des données avec des états de chargement :
import { createSignal, createEffect } from '@emberkit/core';
interface User {
id: number;
name: string;
email: string;
}
function UserList() {
const [users, setUsers] = createSignal<User[]>([]);
const [loading, setLoading] = createSignal(true);
const [error, setError] = createSignal<string | null>(null);
createEffect(async () => {
try {
const res = await fetch('https://api.example.com/users');
const data = await res.json();
setUsers(data);
} catch (err) {
setError('Failed to load users');
} finally {
setLoading(false);
}
});
if (loading()) return <p>Loading...</p>;
if (error()) return <p className="error">{error()}</p>;
return (
<ul>
{users().map(user => (
<li key={user.id}>
<strong>{user.name}</strong> — {user.email}
</li>
))}
</ul>
);
}
Validation de formulaire
Formulaire validé avec affichage des erreurs :
import { createSignal } from '@emberkit/core';
import { createFormValidator, handleFormSubmit } from '@emberkit/core';
const validator = createFormValidator({
fields: {
email: { required: true, pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/ },
password: { required: true, minLength: 8 },
},
});
function LoginForm() {
const [errors, setErrors] = createSignal<Record<string, string>>({});
return (
<form onSubmit={async (e) => {
e.preventDefault();
const form = e.target as HTMLFormElement;
const data = Object.fromEntries(new FormData(form));
const validationErrors = validator.validate(data);
if (Object.keys(validationErrors).length > 0) {
setErrors(validationErrors);
return;
}
await fetch('/api/login', {
method: 'POST',
body: JSON.stringify(data),
});
}}>
<div>
<input name="email" type="email" placeholder="Email" />
{errors().email && <span className="error">{errors().email}</span>}
</div>
<div>
<input name="password" type="password" placeholder="Password" />
{errors().password && <span className="error">{errors().password}</span>}
</div>
<button type="submit">Login</button>
</form>
);
}
Internationalisation (JSON)
Stockez les traductions en JSON et chargez-les avec createI18nFromGlob :
// src/locales/en.json
{
"welcome": "Welcome, {name}",
"nav": { "home": "Home" }
}
// src/lib/i18n.ts
import { createI18nFromGlob, createI18nContext } from '@emberkit/core';
const modules = import.meta.glob('../locales/*.json', { eager: true });
export const i18n = createI18nFromGlob(modules, {
locales: ['en', 'es'] as const,
defaultLocale: 'en',
});
export const { Provider: I18nProvider, useI18n } = createI18nContext();
// src/routes/[locale]/_layout.tsx
import { I18nProvider, i18n } from '../../lib/i18n';
export default function LocaleLayout({ locale, children }) {
return (
<I18nProvider i18n={i18n} locale={locale}>
{children}
</I18nProvider>
);
}
Voir Internationalisation pour la détection de locale SSR, le chargement lazy et les helpers filesystem Node.
Prochaines étapes
- Composants — Patterns de composants
- Signals — État réactif
- Context — Partage d'état
- Internationalisation — Traductions JSON et routage par locale
- Référence API — Documentation API complète