Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/routes/(1)getting-started/(1)project-shapes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ description: "Pick between the bare, basic, and fullstack templates by what your
---

The CLI asks one question before it writes any files: `bare`, `basic`, or `fullstack`.
The answer decides what is installed and what the build produces, and it looks like a commitment.
It is not: each shape is a strict superset of the one before, and the `src/App.tsx` and `src/Document.tsx` conventions are the same in all three, so an app that starts as `bare` becomes `fullstack` by changing configuration rather than rewriting components.
The answer decides what is installed and what the build produces, but you can change it later.
Each shape is a strict superset of the one before, and the `src/App.tsx` and `src/Document.tsx` conventions are the same in all three, so an app that starts as `bare` becomes `fullstack` by changing configuration rather than rewriting components.

Pick by what the app needs on its first deploy:

Expand Down
2 changes: 1 addition & 1 deletion src/routes/(2)concepts/(0)reactivity.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ In the [Quick start](/getting-started/quick-start) you saw two versions of the s
One read `count()` inside the JSX and updated on every click.
The other read `count()` in the component body, and the button froze at zero while the console warned that the read "will not update".

Everything on this page follows from the difference between those two lines.
This page explains why.
Solid does not re-run components.
It runs each component once, records which reactive values each expression reads, and re-runs only those expressions when the values they read change.
The place where you read a value decides whether Solid can see the read.
Expand Down
8 changes: 3 additions & 5 deletions src/routes/(2)concepts/(1)components-and-jsx.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,9 @@ description: "Components as functions that run once: props, children, control fl

A Solid component is a function that runs once.
It sets up state, returns JSX, and is not called again for the life of the component.
If you come from a framework where components re-render, this is the fact to hold on to while reading this page: every rule about props, children, and control flow below exists because the function body will not run a second time.

The [Reactivity](/concepts/reactivity) page explains how the JSX inside a component keeps updating after the function has returned.
This page covers the rest of what a component does: receiving props, handling events, reaching the DOM, rendering lists and conditions, and composing with other components.
The examples continue the shopping cart from that page.
Every rule below about props, children and control flow follows from that: the function body will not run a second time.
[Reactivity](/concepts/reactivity) explains how the JSX keeps updating after the function has returned.
This page covers props, events, refs, lists and conditions, and composition, continuing the shopping cart from that page.

## How JSX executes

Expand Down
11 changes: 4 additions & 7 deletions src/routes/(2)concepts/(2)stores.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,10 @@ version: "2.0"
description: "Hold a cart, a form, or any nested object in a store: update one property through a draft, derive filtered views with a projection, and load server data into the same proxy."
---

The cart on the [Components and JSX](/concepts/components-and-jsx) page held its items in a signal.
That works until the user edits a quantity.
To change one number you must build a new array with one new object in it, and `For` sees a new object where the old row was: the row is torn down and rebuilt, and if the user was typing in that row's input, the input they were typing in is gone.

A store fixes this by tracking each property on its own.
Change `items[1].quantity` and the one text node that reads it updates.
Nothing is copied, no row is rebuilt, and the input keeps focus.
The cart on the [Components and JSX](/concepts/components-and-jsx) page keeps its items in a signal, so changing one quantity means building a new array with a new object in it.
`For` sees a new object where the old row was and rebuilds the row, and the input the user was typing in is replaced.
A store tracks each property on its own instead.
Change `items[1].quantity` and the one text node that reads it updates, and the row and its input stay put.

Use a signal when a value is read and replaced as one unit: a count, a selected id, a string.
Use a store when readers need separate parts of an object or array, which describes most application state.
Expand Down
8 changes: 2 additions & 6 deletions src/routes/(2)concepts/(3)async-reactivity.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,8 @@ version: "2.0"
description: "Read a promise like a value: how async memos hold updates, when Loading shows a fallback, and how mutations settle."
---

Take one value your UI renders today: a product, a list, a setting.
Make it come from the server instead of a constant.
Written by hand, that change spreads: the type becomes `Promise<Product>`, every component between the fetch and the display learns about loading, and the place you fetch and the place you show a spinner become the same place whether you want that or not.

In Solid the change is one line.
A computation that returns a promise is still a computation, and everything that reads it keeps reading a value.
Take a value your UI renders today, such as a product, and load it from the server instead of a constant.
In Solid that is a one-line change: a computation that returns a promise is still a computation, so the type does not become `Promise<Product>`, and the components between the fetch and the display do not need to know about loading.

That one line also changes how writes behave.
Once a memo is async, a write to any input it depends on is held until the memo has its next answer, and everything else in the same update waits with it.
Expand Down
4 changes: 1 addition & 3 deletions src/routes/(2)concepts/(4)boundaries.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@ version: "2.0"
description: "Decide which part of the page shows a spinner, which part shows an error, and in what order regions appear, by placing Loading, Errored, and Reveal around the right subtree."
---

A product page loads the product, its reviews, and a list of recommendations.
With one boundary around the whole page, the first request to fail takes everything down with it, and a slow recommendations query keeps the product itself behind a spinner.
With one boundary around a whole product page, a slow recommendations query keeps the product behind a spinner, and one failed request takes the whole page down.
With no boundary at all, an unhandled error halts the reactive system and nothing on the page updates again.

Boundaries decide how much of the page a pending or failed read affects.
[`Loading`](/reference/solid-js/components-jsx/loading) renders a fallback while async reads in its subtree have no value, [`Errored`](/reference/solid-js/components-jsx/errored) renders a fallback when its subtree throws, and [`Reveal`](/reference/solid-js/components-jsx/reveal) decides the order in which sibling regions appear.
Where you put each one is the design decision this page is about.
Expand Down
11 changes: 4 additions & 7 deletions src/routes/(2)concepts/(5)mutations.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,10 @@ version: "2.0"
description: "Move the cart's add, remove, and set-quantity writes to a server without changing the UI: actions, optimistic stores, refresh, and how a failed request undoes itself."
---

The user clicks **Add to cart**.
The request takes 400 milliseconds.
In that time the cart must show the new line, and when the response arrives the cart must agree with the server; if the server refuses, the line must go away again.

Reads need no wrapper for any of this: change an input and the memo asks again, as the [Async reactivity](/concepts/async-reactivity) page showed.
Writes have a different shape, because a write happens before the round trip and must be reconciled after it.
This page takes the cart from the [Stores](/concepts/stores) page and moves its writes to a server one change at a time.
Reads do not need any wrapper to talk to a server: change an input and the memo asks again, as [Async reactivity](/concepts/async-reactivity) showed.
Writes are harder, because the write happens before the round trip and has to be reconciled after it.
When the user clicks **Add to cart**, the cart should show the new line right away, agree with the server when the response arrives, and drop the line if the server refuses.
This page takes the cart from [Stores](/concepts/stores) and moves its writes to a server one step at a time.

## A client-only cart

Expand Down
2 changes: 1 addition & 1 deletion src/routes/(2)concepts/(6)rendering-and-ssr.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ ReferenceError: window is not defined
```

The stack points at a module that read `localStorage` when it was imported.
Nothing about the component was wrong in the browser; it is now also running in Node, where there is no `window`, and the same source has to be correct in both places.
The component was fine in the browser, but it now also runs in Node, where there is no `window`.

Solid uses one component source for client rendering and server-side rendering (SSR).
The JSX build target selects DOM operations for the browser or HTML-producing operations for the server.
Expand Down
1 change: 0 additions & 1 deletion src/routes/(3)building-apps/(0)app-structure.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ description: "Know which two files in a start-mode project you edit, what the pl
Open the project the [Quick start](/getting-started/quick-start) created and two things are missing that every Vite template has: there is no `index.html`, and no file calls `render()` or `hydrate()`.
What is there is `src/App.tsx`, `src/Document.tsx`, and a `vite.config.ts` with `start: true`.

Those two components are the whole application surface.
`App` is the component every page renders inside; `Document` is the HTML shell around it.
The plugin's start mode generates the rest: the server entry that renders `<Document><App /></Document>`, the client entry that mounts or hydrates it, and the request handler that serves both.

Expand Down
6 changes: 1 addition & 5 deletions src/routes/(3)building-apps/(1)styling-and-assets.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,7 @@ version: "2.0"
description: "Style a component with global CSS, a CSS module, Tailwind, or an inline style object, toggle classes without string building, and reference images so the built URL is hashed."
---

The product card needs a border and the header needs the store logo.
In a Vite single-page app the reflex is a `<link rel="stylesheet">` in `index.html` and an `<img src="/src/logo.svg">`.
A start-mode project has no `index.html`, and `src/` is not served in production, so neither reflex has anywhere to go.

Everything reaches the page through imports instead.
A start-mode project has no `index.html`, and `src/` is not served in production, so stylesheets and images reach the page through imports instead.
Import a stylesheet and Vite includes it in the build; import an image and the import is its final, hashed URL.
Solid adds nothing to that pipeline; what it adds is the `class` and `style` props that apply the result.

Expand Down
5 changes: 1 addition & 4 deletions src/routes/(3)building-apps/(2)head-and-metadata.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@ version: "2.0"
description: "Give each page its own title and meta tags from the component that owns them, let a layout's defaults return when the page unmounts, and read a title from data that is still loading."
---

Every product page in the store has the same browser tab title, "Solid App", because the only `<title>` is the one in `Document.tsx`.
Paste a product link into a chat and the preview shows the same default image for every product.
The component that knows the product name renders inside `<body>`; the tags that need it live in `<head>`, where a component cannot render.

A page title or a link-preview image usually depends on data that only a component deep in `<body>` knows, like the product name, but those tags live in `<head>`, where a component cannot render.
Solid ships a head registry in `@solidjs/web` for this.
A component declares a title or a meta tag from wherever it lives, and the registry puts the tag in the document head, replaces it when a later component declares the same tag, and restores the earlier one when that component unmounts.
`@solidjs/meta` wraps the registry in components.
Expand Down
7 changes: 2 additions & 5 deletions src/routes/(3)building-apps/(3)server-functions/(0)index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,8 @@ version: "2.0"
description: 'Move a database query or a secret out of the browser by marking a function with "use server", and recognize what that call becomes on the wire.'
---

The product page needs the product record.
The record lives in a database, the database client needs a connection string, and neither the client nor the string can ship to the browser.
The usual answer is a separate HTTP API: a route module, a fetch call, a type for the response, and two places to keep in step.

A server function is the same call without the second place.
The product page needs a record from a database, and neither the database client nor its connection string can ship to the browser.
A server function lets the component call that code directly, without a separate HTTP API to write and keep in step.
Add `"use server"` to the function, keep calling it from the component, and the build moves the body to the server and leaves a typed stub in the browser that makes the HTTP request for you.

```ts
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,10 @@ version: "2.0"
description: "Declare a product read that HTTP caches can store, stream order events from a server function, and keep a stock level connected when another shopper changes it."
---

The product page from the [Server functions](/building-apps/server-functions) page shows "3 left in stock".
Another shopper buys one, and the number on this page is wrong until someone reloads.
The same page fetches the product record on every visit, and because a server function call is a `POST` by default, no cache between the browser and the server is allowed to remember the answer.

Both problems are about how a read reaches the server.
A server function call is a `POST` by default, so no cache between the browser and the server can remember the answer, and the value on screen only changes when something asks again.
This page covers the two ways to change how a read reaches the server.
`GET()` declares a read the transport may send as a cacheable `GET` request.
`live()` declares a read that produces values over time and reconnects when the connection drops, so the stock level follows the other shopper's purchase.
`live()` declares a read that produces values over time and reconnects when the connection drops, so a stock count updates when someone else buys the last one.

Most applications need the [GET read](#declare-a-get-read) section, and only when a read is called outside Solid Router's `query()`, plus [live sources](#declare-a-live-source) for a value that changes under the user.
Streams and connection status are for the parts of an app that consume events directly.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@ version: "2.0"
description: "Send arguments a server function can decode, and validate and authorize every call as if it came from a terminal, because it can."
---

The account page lets a shopper change the name on their account.
The component calls `updateAccountName(name)`, TypeScript checks that `name` is a string, and the function writes it to the database.

Every server function is an HTTP endpoint, and the component is one caller among many:
Every server function is an HTTP endpoint, and your component is only one of its callers.
Say the account page calls `updateAccountName(name)` and TypeScript checks that `name` is a string. Anyone can still send this:

```bash
curl -X POST 'https://shop.example/_server/<id>?args=%5B%7B%22admin%22%3Atrue%7D%5D' \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,8 @@ version: "2.0"
description: "Return a value, a redirect, a reload, or a 400 from a cart or account mutation, and control what a thrown error reveals in production."
---

The Add to cart form on the product page posts to `addToCart`.
When the write succeeds, the browser should land on the cart with the new line in it.
When the quantity is not a whole number, the caller should get a 400 and a message it can show next to the field.
When the database is down, the caller should get an error, and that error must not contain the failing query.

A return value can carry the cart line.
It cannot carry a status, a `Location`, or the instruction to refresh the cart, so those go through the response helpers in `@solidjs/web`.
A server function that writes usually needs to say more than its return value can: redirect to the cart after adding to it, answer a bad quantity with a 400 and a message, or fail without leaking the database query.
A status, a `Location` header, or an instruction to refresh the cart goes through the response helpers in `@solidjs/web`.
Most applications need [`redirect()`](#redirect-the-caller), [`reload()`](#request-revalidation), and `throw respond(...)` from [Handle thrown errors](#handle-thrown-errors); the envelope internals are for integrators.

Mutations stay on the default `POST` transport, which is origin-checked.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ description: "Read a server-function request in the network tab, attach a header

Open the network tab and click Add to cart.
The request is a `POST` to `/_server/data/<id>`, it carries `X-Server-Function-Instance` and `X-Server-Function-Format` headers the client runtime added, its body is the argument array, and the response leaves with `Cache-Control: no-store`.
Nothing on the product page asked for any of that; the transport has defaults, and most applications never change them.
Those are the transport's defaults, and most applications never change them.

This page is for the three cases where the defaults are not enough: every request needs a header that changes during the session, such as a bearer token; the endpoint has to live somewhere other than `/_server`; or one call needs to be cancelled while it is in flight.
Most applications need [Prepare every client request](#prepare-every-client-request) and [Configure one call](#configure-one-call).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@ version: "2.0"
description: "Make the Add to cart form post to a server function before JavaScript loads, and know which part of that exchange core owns and which the router adds."
---

A shopper on a slow connection opens the product page and clicks Add to cart before the client bundle has arrived.
If the button's only wiring is an `onClick` handler, nothing happens, and the click is lost.
If the button is inside a form whose `action` is a URL, the browser posts the form, the server runs the function, and the shopper lands on the cart.

A server function has a URL, so the second version is available to every mutation on this cluster's pages.
A form whose `action` is a URL works before your JavaScript loads: the browser posts it, the server runs the function, and the user lands on the next page.
An `onClick` handler does nothing until the bundle arrives.
Every server function has a URL, so any mutation can work the first way.
The server-function runtime recognizes a browser form post at that URL, decodes the body as one `FormData` argument, and answers in a way the browser can follow with no script.

Application code reaches this through a router.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,9 @@ version: "2.0"
description: "Forward a model's token stream through a server function into a store one chunk at a time, stop it from the browser or by asking a new question, and render the Markdown without re-rendering what has already landed."
---

The storefront gets a support assistant: a shopper types a question and the answer appears word by word as the model produces it.
Halfway through, the shopper asks a different question, or presses Stop, or the model call fails and they want to try again.
Written by hand, that is a `fetch` with a body reader, a flag for "streaming", an `AbortController` kept somewhere, and a text state that is replaced on every chunk.

In Solid the model call is a server function that returns an async generator, the browser side is a projection that writes one store commit per chunk, and cancellation is what the reactive graph does with a computation it no longer needs.
This guide builds that assistant and ends with the cancellation contract, because deciding who may stop a generation is the part that does not fall out of the code.
This guide builds a support assistant whose answer appears word by word, and that the shopper can stop, retry, or interrupt with a new question.
The model call is a server function that returns an async generator, the browser side is a projection that writes one store update per chunk, and cancelling is what the reactive graph already does with a computation it no longer needs.
The guide ends with who is allowed to stop a generation, because that is the one part you have to decide yourself.

## Stream from the server

Expand Down
Loading
Loading