App Routes

App routes are fully dynamic, server-side rendered (SSR) routes that hydrate on the client. They function like a Single Page Application (SPA) after the initial load.

Characteristics

  • SSR: HTML is generated on the server for every request.
  • Hydration: Preact hydrates the page on the client, making it fully interactive.
  • Client-side Navigation: Clicking links uses client-side routing, avoiding full page reloads.
  • Shared State: Layouts persist state during navigation.

Configuration

To make a route an app route, set the mode to app:

export const config = { mode: "app" };

Runtime Choice

Neutron allows you to choose the underlying runtime for your app routes.

  • Preact (Default): Optimized for performance and size (~3KB runtime). Highly compatible with the React ecosystem.
  • React: For apps that require specific React internals or libraries that are not fully compatible with Preact.

You can configure this in neutron.config.ts:

export default defineConfig({
  runtime: "preact", // or "react"
});

Regardless of the runtime, your code uses standard React-style hooks and JSX.

Data Loading

App routes use loader functions to fetch data on the server.

import { useLoaderData } from "neutron";

export const config = { mode: "app" };

export async function loader({ request }: LoaderArgs) {
  const user = await getUser(request);
  return { user };
}

export default function Profile() {
  const { user } = useLoaderData<typeof loader>();
  return <h1>Hello, {user.name}</h1>;
}

Mutations

App routes use action functions to handle form submissions and data mutations.

import { Form } from "neutron";

export async function action({ request }: ActionArgs) {
  const formData = await request.formData();
  // ... handle update
  return { success: true };
}

export default function Settings() {
  return (
    <Form method="post">
      <button>Update</button>
    </Form>
  );
}

See the Data Loading and Actions guides for more details.