#React Integration
Alepha provides first-class React support with server-side rendering, dependency injection in components, type-safe API calls, and a global state system.
#Project Setup
Scaffold a React project with the Alepha CLI:
alepha init --react
This generates two entry points:
src/main.server.ts(ormain.ts) -- server entry, registers modules and starts the appsrc/main.browser.ts-- browser entry, registers browser-side modules and hydrates
Server entry (main.ts):
1import { Alepha, run } from "alepha"; 2import { AppRouter } from "./AppRouter.ts"; 3import { CountApi } from "./CountApi.ts"; 4 5const alepha = Alepha.create({ 6 env: { APP_NAME: "MY_APP" }, 7}); 8 9alepha.with(CountApi);10alepha.with(AppRouter);11 12run(alepha);
Browser entry (main.browser.ts):
1import { Alepha, run } from "alepha";2import { AppRouter } from "./AppRouter.ts";3 4const alepha = Alepha.create();5alepha.with(AppRouter);6 7run(alepha);
The browser entry only registers the modules needed on the client (e.g., routes, UI). Server-only modules like API controllers are excluded.
#Core Hooks
All core React hooks are imported from "alepha/react".
#useAlepha
Returns the current Alepha instance from context. Provides access to the DI container, event system, and store.
1import { useAlepha } from "alepha/react";2 3function MyComponent() {4 const alepha = useAlepha();5 // alepha.inject(SomeService)6 // alepha.events.emit(...)7 // alepha.store.get(...)8}
Must be used within an Alepha context (provided automatically by the router or by <AlephaProvider>).
#useInject
Injects a DI service into a React component. The service must be registered with the Alepha instance. Result is memoized.
1import { useInject } from "alepha/react";2 3function Dashboard() {4 const analytics = useInject(AnalyticsService);5 // use analytics methods6}
#useClient
Type-safe API calls from React. Connects to server-side controllers via the link system. Works with SSR -- on the server, calls are made internally without HTTP.
1import { useAction, useClient } from "alepha/react"; 2import { useState } from "react"; 3import type { CountApi } from "./CountApi.ts"; 4 5const Home = (props: { count: number }) => { 6 const [count, setCount] = useState(props.count); 7 const countApi = useClient<CountApi>(); 8 9 const inc = useAction(10 {11 handler: async () => {12 const result = await countApi.inc();13 setCount(result.count);14 },15 },16 [count],17 );18 19 return <button onClick={inc.run}>Click {count}</button>;20};
The type parameter <CountApi> provides full type safety -- method names, parameter types, and return types are all inferred from the controller class.
#useAction
Manages async operations with loading state, error handling, cancellation, debounce, and polling.
1import { useAction } from "alepha/react";
Returns: { run, loading, error, cancel, result }
| Property | Type | Description |
|---|---|---|
run |
(...args) => Promise |
Execute the action |
loading |
boolean |
True while executing |
error |
Error | undefined |
Error from last failed execution |
cancel |
() => void |
Cancel debounce timer or abort in-flight |
result |
T | undefined |
Result from last successful execution |
Options:
| Option | Type | Description |
|---|---|---|
handler |
(...args, ctx) => Promise |
The async function to execute. Receives an ActionContext with an AbortSignal as the last argument. |
onError |
(error) => void |
Custom error handler. Prevents re-throw if set. |
onSuccess |
(result) => void |
Called after successful execution. |
id |
string |
Identifier for debugging and analytics. |
debounce |
number |
Delay in milliseconds before executing. |
runOnInit |
boolean |
Run once when the component mounts. |
runEvery |
DurationLike |
Run periodically at the given interval. |
By default, concurrent executions are prevented -- calling run while already executing is a no-op.
Debounce example (search input):
1const search = useAction( 2 { 3 handler: async (query: string) => { 4 return await api.search(query); 5 }, 6 debounce: 300, 7 }, 8 [], 9);10 11// <input onChange={(e) => search.run(e.target.value)} />
Polling example:
1const pollStatus = useAction( 2 { 3 handler: async () => { 4 return await api.getStatus(); 5 }, 6 runEvery: 5000, 7 }, 8 [], 9);10 11// Or with duration tuple:12// runEvery: [30, "seconds"]
AbortController example:
1const fetchData = useAction( 2 { 3 handler: async (url: string, { signal }: { signal: AbortSignal }) => { 4 const response = await fetch(url, { signal }); 5 return response.json(); 6 }, 7 }, 8 [], 9);10// Automatically cancelled on unmount or when a new request starts
Lifecycle events:
Actions emit events on the Alepha event system:
react:action:begin-- action startedreact:action:success-- action completed successfullyreact:action:error-- action threw an errorreact:action:end-- always emitted at the end
Global error handling example:
1alepha.events.on("react:action:error", ({ error }) => {2 toast.danger(error.message);3});
#useEvents
Subscribe to Alepha events inside React components. Subscriptions are automatically cleaned up on unmount.
1import { useEvents } from "alepha/react"; 2 3function StatusBar() { 4 useEvents( 5 { 6 "react:transition:begin": (ev) => { 7 console.log("Navigating to:", ev.state.url.pathname); 8 }, 9 "react:action:error": (ev) => {10 console.error("Action failed:", ev.error);11 },12 },13 [],14 );15 16 return <div>...</div>;17}
The second argument is a dependency list (same as useEffect). Events are fully typed based on the Hooks interface.
#AlephaProvider
Only needed if you are not using the Alepha Router (e.g., in Expo or Next.js integrations). When using $page and the router, the context is provided automatically.
1import { AlephaProvider } from "alepha/react"; 2 3function App() { 4 return ( 5 <AlephaProvider 6 onLoading={() => <div>Loading...</div>} 7 onError={(error) => <div>Error: {error.message}</div>} 8 > 9 <MyApp />10 </AlephaProvider>11 );12}
AlephaProvider creates an Alepha instance, calls start(), and provides the instance via React context. Props:
| Prop | Type | Description |
|---|---|---|
children |
ReactNode |
Application content |
onLoading |
() => ReactNode |
Rendered while Alepha is starting |
onError |
(error: Error) => ReactNode |
Rendered if start fails |