DocsFramework differences
Preact
Nexy renders Preact 10 components on the server, then Preact takes them over in the browser. Pages, layouts, islands and actions are the same in every framework: this page covers setup, then the parts that differ from the shared contracts.
With the modular router, Preact components are islands inside
the .nexy views a controller returns: there is no page file and no client router.
Start a project
nx new my-app --template web-fbr-preact
nx new my-app --template web-modular-preact
The template is a server-mode project with Preact islands. Run nx new my-app without a template to
answer the prompts instead: the router, the project type, whether to use client-side routing, then
the framework.
Enable Preact in an existing project
from nexy.core.models import NexyConfigModelfrom nexy.frontend import preact class NexyConfig(NexyConfigModel): useFF = preact() useAliases = {"@": "src"}import { defineConfig } from "vite";import preact from "@preact/preset-vite";import nexy from "./__nexy__/vite"; export default defineConfig({ plugins: [nexy(), preact()], customLogger: nexy.log(),});nx new writes this file; Nexy writes __nexy__/vite.ts itself, so do not edit it.
| Package | Version nx new writes |
|---|---|
preact |
^10.25.4 |
@preact/preset-vite |
^2.10.1 (dev dependency) |
preact-render-to-string |
^6.7.0 (dev dependency, renders on the server) |
Write Preact
A Preact component is a React-style function. The differences that matter here:
import { useState } from "preact/hooks"; export default function Counter({ start = 0 }: { start?: number }) { const [count, setCount] = useState(start); return <button class="counter" onClick={() => setCount(count + 1)}>{count}</button>;}- Hooks come from
preact/hooks, types such asComponentChildrenfrompreact. - Write HTML attributes:
class,for,charset.Linkreads its classes fromclass.
Rules
- One framework per project.
useFFtakes exactly one framework;useFF = [preact(), react()]stops the server at startup. WithuseFF = preact(), every.tsxfile is Preact.
- Pages are
.tsx. Only a.tsxfile undersrc/routes/becomes a page;.jsxis fine for a component. - Components live outside
src/routes/, usually insrc/components/, so they are not pages. - A server-mode project has no
.tsxpage, and a client-mode project has no.nexypage: Nexy refuses to start and names the file. See modes.
- Components live in
src/components/, and a view imports them as islands.
- An
.mdxpage imports from"preact". Nexy compiles an.mdxfile with Preact only when it sees that import, for exampleimport { h } from "preact", and with React otherwise. See Markdown and MDX.
Pages
In client mode (useCSR = True), a page is a .tsx file.
Page component
A page is the default export of the file. If no default export exists, Nexy picks the first named export.
export default function Home() { return <h1>Hello</h1>;}Dynamic segments
URL parameters are available via the useParams hook:
import { useParams } from "@nexy/client"; export default function Post() { const { slug } = useParams<{ slug: string }>(); return <h1>{slug}</h1>;}Layouts
Implementation
The root layout defines the document shell. Nested layouts wrap the content.
import type { ComponentChildren } from "preact"; export default function RootLayout({ children }: { children: ComponentChildren }) { return ( <html> <body> <header>My app</header> <main>{children}</main> </body> </html> );}Islands
In server mode, a Preact component imported into a .nexy file is an island.
Importing
Import components in the frontmatter of the .nexy file:
from "@/components/Counter.tsx" import Counter<Counter start=3 />from "@/components/Counter.tsx" import Counter<Counter start=3 />Hydration directives
Use the directive prop to control when the island hydrates:
client:load(default): immediatelyclient:idle: when the browser is idleclient:visible: when it scrolls into viewclient:media: when the media query matches
Actions and navigation
Calling actions
Use the useAction hook to manage the state of an action call:
import { useAction } from "@nexy/client";import { add_todo } from "@nexy/actions"; const { run, pending, error, data } = useAction(add_todo);// ...<button onClick={() => run({ title: "New Task" })}>Add</button>
Navigation
Use the Link component for client-side transitions. Note that Preact uses class instead of
className:
import { Link } from "@nexy/client"; <Link href="/about" class="nav" activeClassName="active">About</Link>
For programmatic navigation, use the useRouter hook:
import { useRouter } from "@nexy/client"; const router = useRouter();router.push("/dashboard");
URL access
usePathname(): current pathuseParams(): dynamic segmentsuseSearchParams(): query stringuseLocation(): full location object