DocsFramework differences

Solid

Nexy renders Solid 1.9 components on the server, then Solid 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.

Mode nexyconfig.py Pages are Solid is
Server (default) useCSR = False .nexy files islands inside those pages
Client useCSR = True .tsx files the whole page, with layouts and a client router

With the modular router, Solid 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-solid
nx new my-app --template web-modular-solid

The template is a server-mode project with Solid 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 Solid in an existing project

nexyconfig.py
from nexy.core.models import NexyConfigModelfrom nexy.frontend import solid  class NexyConfig(NexyConfigModel):    useFF = solid()    useAliases = {"@": "src"}
vite.config.ts
import { defineConfig } from "vite";import solid from "vite-plugin-solid";import nexy from "./__nexy__/vite"; export default defineConfig({  plugins: [nexy(), solid()],  customLogger: nexy.log(),});

In client mode write solid({ ssr: true }): Solid compiles a component differently for the server and for the browser, and client mode needs both. nx new writes this file; Nexy writes __nexy__/vite.ts itself, so do not edit it.

Package Version nx new writes
solid-js ^1.9.12
vite-plugin-solid ^2.11.0 (dev dependency)

Think in signals

A Solid component function runs once. What changes later is read through a signal, and only the expressions that read it update:

src/components/Counter.tsx
import { createSignal } from "solid-js"; export default function Counter(props: { start?: number }) {  const [count, setCount] = createSignal(props.start ?? 0);  return <button onClick={() => setCount(count() + 1)}>{count()}</button>;}
  • Call the signal, count(), inside the JSX, not in a variable above it.
  • Read props as props.start. Destructuring { start } reads the value once and loses the updates.
  • Solid writes HTML attributes: class, for, charset, not className.

Rules

  • One framework per project. useFF takes exactly one framework; useFF = [solid(), react()] stops the server at startup. With useFF = solid(), every .tsx file is Solid.
  • Pages are .tsx. Only a .tsx file under src/routes/ becomes a page; .jsx is fine for a component.
  • Components live outside src/routes/, usually in src/components/, so they are not pages.
  • A server-mode project has no .tsx page, and a client-mode project has no .nexy page: Nexy refuses to start and names the file. See modes.
  • Components live in src/components/, and a view imports them as islands.
  • No .mdx with Solid. MDX compiles to React or Preact; .md pages work.

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.

src/routes/index.tsx
export default function Home() {  return <h1>Hello</h1>;}

Dynamic segments

URL parameters are available via the useParams hook, which returns an accessor:

src/routes/blog/[slug].tsx
import { useParams } from "@nexy/client"; export default function Post() {  const params = useParams<{ slug: string }>();  return <h1>{params().slug}</h1>;}

Layouts

Implementation

The root layout defines the document shell. Nested layouts wrap the content.

src/routes/layout.tsx
import type { JSX } from "solid-js"; export default function RootLayout(props: { children: JSX.Element }) {  return (    <html>      <body>        <header>My app</header>        <main>{props.children}</main>      </body>    </html>  );}

Do not destructure props.children, as Solid reads it lazily.

Islands

In server mode, a Solid component imported into a .nexy file is an island.

Importing

Import components in the frontmatter of the .nexy file:

src/routes/index.nexy
---from "@/components/Counter.tsx" import Counter---<Counter start=3 />
src/apps/home_view.nexy
---from "@/components/Counter.tsx" import Counter---<Counter start=3 />

Hydration directives

Use the directive prop to control when the island hydrates:

  • client:load (default): immediately
  • client:idle: when the browser is idle
  • client:visible: when it scrolls into view
  • client:media: when the media query matches

Actions and navigation

Calling actions

Use the useAction hook to manage the state of an action call. In Solid, this returns accessors (call them as functions).

import { useAction } from "@nexy/client";import { add_todo } from "@nexy/actions"; const save = useAction(add_todo);// ...<button disabled={save.pending()}>Add</button>

Use the Link component for client-side transitions:

import { Link } from "@nexy/client"; <Link href="/about">About</Link>

For programmatic navigation, use the useRouter hook:

import { useRouter } from "@nexy/client"; const router = useRouter();router.push("/dashboard");

URL access

All location hooks return accessors:

  • usePathname(): current path
  • useParams(): dynamic segments
  • useSearchParams(): query string
  • useLocation(): full location object

Next steps

  • Views: compare the formats a page can use
  • Frontend: Vite and the client build

Edit on GitHub

Enter opens the result, Esc closes.