smolter
Guides

Getting started

Choose React or Preact, register your routes, and mount the provider.

Compatibility

ReactAvailable in this renderer's browser integration.
React SSRAvailable to this renderer's SSR integration; browser event handlers run after hydration.
PreactAvailable in this renderer's browser integration.
Preact SSRAvailable to this renderer's SSR integration; browser event handlers run after hydration.

Install

Install the router and the framework you use. Framework peers are optional; choosing React does not install Preact. Smolter’s Vite plugin uses its own pinned stable TypeScript 6 compiler API (typescript-compiler) for typed prefetch selection. Your application can keep TypeScript 7; no compiler downgrade or peer installation is required. The compiler does not enter browser bundles.

pnpm add @kasperrt/smolter react react-dom
# Or: pnpm add @kasperrt/smolter preact
# With Smolter’s Vite plugin:
pnpm add -D vite typescript

Define the data

Put browser reads in clientLoader.ts. Replace this array with a request to your API when needed.

// routes/tasks/clientLoader.ts
export function clientLoader() {
  return { tasks: [{ id: "1", title: "Try smolter" }] };
}

Render the result

Re-export the loader from the route module. The type-only import connects useLoaderData to its return type.

// routes/tasks/route.tsx
import { useLoaderData } from "@kasperrt/smolter/react";
import type { clientLoader } from "./clientLoader";

export { clientLoader } from "./clientLoader";

export default function Tasks() {
  const data = useLoaderData<typeof clientLoader>();

  return <ul>{data?.tasks.map((task) => (
    <li key={task.id}>{task.title}</li>
  ))}</ul>;
}

Register a route

Keep the data handler beside the page. defineRoute retains its handler types. Manual routes use the first matching pattern, so put static paths before dynamic paths.

import { createDataRouter, defineRoute } from "@kasperrt/smolter/react";
import * as tasks from "./routes/tasks/route";

export const router = createDataRouter([
  defineRoute("/tasks", tasks),
], { browser: true });

Render

Create the router outside render. RouterProvider loads the current route; Routes renders its view. Mount App with React’s createRoot or Preact’s render, then open /tasks.

import { RouterProvider, Routes } from "@kasperrt/smolter/react";
import { router } from "./router";

export function App() {
  return <RouterProvider router={router}>
    <Routes fallback={<p>Page not found.</p>} />
  </RouterProvider>;
}

Next steps

Continue with the guides for reading data, handling mutations and background requests.

Loaders →

Actions →

Fetchers →