lunx.docs
DocsFrameworksSolidStart

SolidStart Adapter

SolidJS with SolidStart meta-framework: fine-grained reactivity, no VDOM, server functions, file-based routing, and streaming SSR with renderToStream — TTFB under 2ms.

Solid 1.8 · No VDOM · Streaming SSR
Fine-grained reactivity

Signals update the exact DOM node that changed — no VDOM diffing, no component re-renders.

renderToStream()

Streaming SSR with Suspense support. TTFB under 2ms. Content streams as data resolves.

Server functions

'use server' directive marks functions that run exclusively on the server — called like regular functions.

File routing

routes/ directory generates routes automatically. Nested layouts with createRouteData().

Zero overhead

Solid compiles to precise DOM operations. Bundles are smaller than React with better runtime perf.

TypeScript native

Full TypeScript support including JSX types via solid-js/types.

Setup

bash
npm create lunx-dev@latest my-solid-app -- --framework solid --ts
cd my-solid-app && npm install && npm run dev

Solid reactivity with signals

typescriptsrc/components/Counter.tsx
import { createSignal, createMemo, createEffect } from 'solid-js'

export function Counter() {
  // Signals — like React useState but update DOM directly
  const [count, setCount]  = createSignal(0)
  const [name,  setName]   = createSignal('World')

  // Memo — like useMemo but reactive automatically
  const doubled   = createMemo(() => count() * 2)
  const greeting  = createMemo(() => `Hello ${name()}! Count: ${count()}`)

  // Effect — runs when dependencies change
  createEffect(() => {
    document.title = `Count: ${count()}`
  })

  return (
    <div>
      <h2>{greeting()}</h2>
      <p>Doubled: {doubled()}</p>
      <input value={name()} onInput={e => setName(e.target.value)} />
      <button onClick={() => setCount(c => c - 1)}>−</button>
      <button onClick={() => setCount(c => c + 1)}>+</button>
    </div>
  )
}

Server functions with 'use server'

typescriptsrc/routes/blog/index.tsx
import { createRouteData, useRouteData, refetchRouteData } from 'solid-start'
import { createServerAction$ }  from 'solid-start/server'

// Runs on server only — called like a regular function from client
async function getPosts() {
  'use server'
  return await db.post.findMany({ orderBy: { createdAt: 'desc' } })
}

export function routeData() {
  return createRouteData(getPosts, { key: 'posts' })
}

export default function BlogIndex() {
  const posts = useRouteData<typeof routeData>()
  const [addPost, { Form }] = createServerAction$(async (form: FormData) => {
    'use server'
    const title = form.get('title') as string
    await db.post.create({ data: { title, slug: slugify(title) } })
  })

  return (
    <div>
      <Form>
        <input name="title" placeholder="New post title..." />
        <button type="submit">Add Post</button>
      </Form>

      <Suspense fallback={<p>Loading...</p>}>
        <For each={posts()}>
          {post => <article><h2>{post.title}</h2></article>}
        </For>
      </Suspense>
    </div>
  )
}
Why Solid over React?
SolidJS uses the same JSX syntax as React but compiles to direct DOM operations — no virtual DOM reconciliation. A counter component in Solid updates a single text node; in React it re-executes the entire component function. This makes Solid ~2–3× faster at runtime for highly dynamic UIs.