[]previous and next page

Project layout

<html> and <head> are not part of a component: Dusk writes the document shell.

For agentsUpdated
text
app/layout.tsx          ->  src/app.tsx and src/routes/_layout.tsx
app/page.tsx            ->  src/routes/index.tsx
app/posts/[id]/page.tsx ->  src/routes/posts/[id].tsx
app/api/x/route.ts      ->  src/routes/api/x.ts
middleware.ts           ->  src/entry-server.ts   (the `middleware` option)
next.config.js          ->  vite.config.ts        (dusk options)
public/                 ->  public/               (same)
.env.local              ->  .env.local            (see Environment variables)

<html> and <head> are not part of a component: Dusk writes the document shell. document.lang sets <html lang>, route head exports write the tags, and document.head adds trusted markup to every page. The root component src/app.tsx wraps the routed page.

json
{
  "scripts": {
    "dev": "dusk dev",
    "build": "dusk build",
    "start": "dusk start"
  }
}

next dev becomes dusk dev, next build becomes dusk build, and next start becomes dusk start. The dusk command runs Vite underneath, so the file watching and hot reload are Vite's, as in any Vite app. .env files load the same way (see Environment variables). dusk start serves the build with the Node adapter, which the config below enables; The dusk command lists the flags. The files of the app used on this page:

vite.config.tsts
import { node } from "@shadow-js/dusk/node";
import { dusk } from "@shadow-js/dusk/vite";
import { defineConfig } from "vite";

export default defineConfig({ plugins: [dusk({ adapter: node() })] });
src/app.tsxtsx
import type { JSX } from "@shadow-js/web";

export default function App(props: { children?: JSX.Element }): JSX.Element {
  return <div class="app">{props.children}</div>;
}

src/schema.ts is a stand-in for Zod, Valibot or ArkType: server functions take any Standard Schema.

src/schema.tsts
import type { StandardSchemaV1 } from "@shadow-js/dusk/server";

/** Wraps a parse function that throws on bad input as a Standard Schema. */
export function schema<T>(parse: (value: unknown) => T): StandardSchemaV1<T, T> {
  return {
    "~standard": {
      version: 1,
      vendor: "app",
      validate(value) {
        try {
          return { value: parse(value) };
        } catch (error) {
          return { issues: [{ message: error instanceof Error ? error.message : "invalid" }] };
        }
      },
    },
  };
}