App mode is for routes whose response depends on the request or whose interface needs the client router. Neutron runs matching middleware and loaders on the server, renders HTML, then hydrates the route in the browser.

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

After the initial document load, links between app routes use client-side navigation. A project can mix app routes with static routes; navigation to a static route remains a browser-native document navigation.

Load server data

Matching layout and page loaders run in parallel. Their return values are serialized for the route components:

import { useLoaderData } from "@neutron-build/core";

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

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

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

Keep credentials and private services inside loaders and actions; those exports are stripped from the client build.

Handle mutations

An action handles non-GET submissions for the matched route:

import { Form } from "@neutron-build/core";

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

export async function action({ request }) {
  const form = await request.formData();
  await updateName(form.get("name"));
  return { saved: true };
}

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

See Loaders, Actions, and Forms.

Client entry

The generated src/main.tsx registers the shared route table and starts hydration:

import { init, registerRoutes } from "@neutron-build/core/client";
import { routes } from "virtual:neutron/routes";

registerRoutes(routes);
void init();

The starter creates this file. Add browser-only setup before init() when needed; removing the entry disables hydration and client navigation.

Runtime

Preact is the default. Projects that need React package compatibility can use the react-compat runtime when they are created:

npm create neutron@latest my-app -- --runtime react-compat

The runtime setting is project-wide; the static/app decision is per route.