Skip to Content
InitializerOverview

@lamstack/react-initializer

A lightweight application-startup orchestrator for React. <Initializer> runs a sequence of stages — a task, or several running concurrently via parallel([...]) — before rendering your app, with retry, timeout, and critical/non-critical failure handling built in. The task runner has no React dependency; the React layer is a thin adapter on top of it.

Pre-1.0 (0.x): the API may still change between minor versions. See Non-goals below for what this library deliberately doesn’t do.

pnpm add @lamstack/react-initializer

Live demo

Three stages: config runs first, then auth (critical — its failure aborts the whole run), then a parallel([...]) stage of profile/cache/translations — since auth is a stage of its own, that whole third stage simply never starts if auth fails. translations is marked critical: false and always fails on its own — try it with the checkbox off to see a non-critical failure recorded without blocking the app (in a real app you’d fall back to a default locale here).

    Quickstart

    The minimal version — no custom splash/error UI, no parallel(), just tasks in order:

    import { Initializer, useInitializer } from '@lamstack/react-initializer'; import type { InitializationTask } from '@lamstack/react-initializer'; const initializeConfig: InitializationTask = { id: 'config', run: async ({ state }) => { state.set('config', await loadConfig()); }, }; const initializeAuth: InitializationTask = { id: 'auth', retry: 3, run: async () => { await restoreSession(); }, }; function App() { return ( <Initializer tasks={[initializeConfig, initializeAuth]}> <Dashboard /> </Initializer> ); } function Dashboard() { const { retry } = useInitializer(); return <button onClick={retry}>Reload app</button>; }

    <Initializer> withholds children until every task has settled: it renders a splash screen while tasks run, and an error screen if a critical one fails. Both are plain, overridable defaults — pass your own splashScreen/errorScreen components, as the demo above does, to replace them.

    • Concepts — the stage model, critical vs non-critical failures, retry/timeout, condition, shared state, and the rendering contract.
    • API referenceInitializationTask, parallel(), <Initializer> props, useInitializer(), and the framework-independent createInitializer().
    • Testing — how to test components that call useInitializer(), and the task runner in isolation.

    Non-goals

    @lamstack/react-initializer does exactly one thing: run a sequence of async startup steps before rendering the app. It is deliberately not:

    • A data-fetching layer. No caching, no request deduplication, no revalidation. Fetch in run, store the result in state if a later stage needs it, and use a real data-fetching library for anything your app needs after startup.
    • A background job scheduler. Tasks run once, during the one run a <Initializer> instance (or createInitializer() call) drives — there’s no recurring/scheduled execution.
    • A general dependency-graph runner. Ordering is exactly two levels: the stage list, and parallel([...]) within a stage. There is no dependsOn, no cross-stage edges, and no cycle detection to speak of, because there’s no graph to have a cycle in.
    • SSR/Suspense-integrated, and it has no cross-reload caching — every mount (or retry()) is a fresh run, fresh state, fresh AbortController.

    If you need any of the above, reach for a purpose-built tool instead and hand its result to a state.set(...) call from within a task.

    Last updated on