---
name: shadowjs
description: "Build and fix user interfaces with ShadowJS: JSX components that run once, signals (useSignal, useStore, useMemo, useEffect), the DOM runtime in @shadow-js/web, and the Vite plugin. Use it when a project imports @shadow-js/core or @shadow-js/web, or when asked to write, review or debug a ShadowJS component."
---

# ShadowJS

ShadowJS compiles JSX into direct DOM updates. A component function runs once, when it is created. State is held in signals, read by calling them, and a change updates only the DOM nodes and computations that read it. There is no virtual DOM and nothing to diff.

The runtime is two packages: `@shadow-js/core` (signals, memos, effects, ownership, resources, context) and `@shadow-js/web` (the DOM renderer, control-flow components, server rendering, hydration). The compiler runs in the build through `@shadow-js/vite`.

## When to use

- The project imports `@shadow-js/core` or `@shadow-js/web`, or has `@shadow-js/vite` in `vite.config.ts`.
- You are asked to write, change or review a component, a hook-style function (`useSignal`, `useMemo`) or a control-flow element (`Show`, `For`).
- A runtime message starts with `[shadow CODE]`: look the code up in the diagnostics reference.

## Install

```sh
npm create shadow-app@latest my-app
npm install @shadow-js/core @shadow-js/web
npm install -D @shadow-js/vite
```

## Project layout

`npm create shadow-app@latest my-app` writes:

```text
.gitignore
README.md
index.html
package.json
src/App.tsx
src/index.css
src/main.tsx
src/vite-env.d.ts
tsconfig.json
vite.config.ts
```

## API

### State

#### `useSignal`

Creates a reactive cell holding one value: the low-level primitive `useStore` builds on.

```ts
import { useSignal } from "@shadow-js/core";

function useSignal<T>(value: T, options?: SignalOptions<T>): [Accessor<T>, Setter<T>]
```

#### `useStore`

The state hook.

```ts
import { useStore } from "@shadow-js/core";

function useStore<T extends NotMixed<T>>(initial: T, options?: StoreOptions): StoreResult<T>

function useStore<T>(initial: MixedInitial<T>, options?: StoreOptions): StoreResult<T>
```

#### `useMemo`

Creates a cached derivation that recomputes only when a dependency actually changed.

```ts
import { useMemo } from "@shadow-js/core";

function useMemo<T>(
  fn: (previous: T | undefined) => T,
  options?: MemoOptions<T>,
): Accessor<T>
```

#### `useEffect`

Runs a side effect after rendering and again whenever a tracked signal changes.

```ts
import { useEffect } from "@shadow-js/core";

function useEffect(fn: () => void | Cleanup, options?: EffectOptions): void

function useEffect<T>(
  deps: () => T,
  fn: (value: T, previous: T | undefined) => void | Cleanup,
  options?: DeferredEffectOptions,
): void
```

#### `onMount`

Runs `fn` once after the owner has been created and flushed, untracked, so signal changes never run it again.

```ts
import { onMount } from "@shadow-js/core";

function onMount(fn: () => void | Cleanup): void
```

#### `onCleanup`

Registers `fn` to run when the current owner is disposed or, for an effect or memo, before it re-runs.

```ts
import { onCleanup } from "@shadow-js/core";

function onCleanup(fn: Cleanup): void
```

#### `batch`

Runs `fn` with effects deferred until the outermost batch ends, then runs them synchronously, so several writes wake each effect once.

```ts
import { batch } from "@shadow-js/core";

function batch<T>(fn: () => T): T
```

#### `untrack`

Runs `fn` without subscribing the running computation to anything `fn` reads.

```ts
import { untrack } from "@shadow-js/core";

function untrack<T>(fn: () => T): T
```

### Data and context

#### `useResource`

Loads data asynchronously and exposes it reactively.

```ts
import { useResource } from "@shadow-js/core";

function useResource<T>(
  fetcher: (info: ResourceInfo) => T | Promise<T>,
  options?: ResourceOptions<T>,
): Resource<T>

function useResource<S, T>(
  source: Accessor<S | false | null | undefined>,
  fetcher: (source: S, info: ResourceInfo) => T | Promise<T>,
  options?: ResourceOptions<T>,
): Resource<T>

function useResource<S extends string | number | boolean | bigint | object, T>(
  source: S,
  fetcher: (source: S, info: ResourceInfo) => T | Promise<T>,
  options?: ResourceOptions<T>,
): Resource<T>

function useResource<T>(
  fetcher: (info: ResourceInfo<T>) => T | Promise<T>,
  options?: ResourceOptions<T>,
): Resource<T>

function useResource<S, T>(
  source: Accessor<S | false | null | undefined>,
  fetcher: (source: S, info: ResourceInfo<T>) => T | Promise<T>,
  options?: ResourceOptions<T>,
): Resource<T>
```

#### `createContext`

Creates a context: a value that components read with {@link useContext} without passing it through props.

```ts
import { createContext } from "@shadow-js/core";

function createContext<T>(defaultValue?: T, options?: ContextOptions): Context<T>
```

#### `useContext`

Reads the nearest provided value of `context`.

```ts
import { useContext } from "@shadow-js/core";

function useContext<T>(context: Context<T>): T
```

#### `useOptimistic`

Optimistic state: shows `reducer` applied over the real value for every action whose `settled` promise is still pending.

```ts
import { useOptimistic } from "@shadow-js/core";

function useOptimistic<T, A>(
  source: Accessor<T>,
  reducer: (state: T, action: A) => T,
): [Accessor<T>, (action: A, settled: Promise<unknown>) => void]
```

### Rendering

#### `render`

Mounts `code` into `container`.

```ts
import { render } from "@shadow-js/web";

function render(
  code: () => unknown,
  container: Element | Document | DocumentFragment,
  options?: RenderOptions,
): () => void
```

#### `hydrate`

Attaches the application to HTML the server already rendered into `container`.

```ts
import { hydrate } from "@shadow-js/web";

function hydrate(
  code: () => unknown,
  container: Element | Document,
  options: HydrateOptions = {},
): () => void
```

#### `Show`

Shows `children` while `when` is truthy, and `fallback` otherwise.

```ts
import { Show } from "@shadow-js/web";

function Show<T>(props: ShowProps<T>): Accessor<unknown>

function Show<T>(props: ShowLooseProps<T>): Accessor<unknown>
```

#### `For`

Renders one row per item.

```ts
import { For } from "@shadow-js/web";

function For<T>(props: ForKeyedProps<T>): Accessor<unknown[]>

function For<T>(props: ForUnkeyedProps<T>): Accessor<unknown[]>
```

#### `Index`

Renders one row per position.

```ts
import { Index } from "@shadow-js/web";

function Index<T>(props: IndexProps<T>): Accessor<unknown[]>
```

#### `Switch`

Shows the first {@link Match} whose `when` is truthy, or the fallback when none is.

```ts
import { Switch } from "@shadow-js/web";

function Switch(props: SwitchProps): Accessor<unknown>
```

#### `Match`

Describes one branch of a {@link Switch}.

```ts
import { Match } from "@shadow-js/web";

function Match<T>(props: MatchProps<T>): MatchProps<T>
```

#### `Dynamic`

Renders a component function or an element chosen by a tag name, and re-renders when `component` changes.

```ts
import { Dynamic } from "@shadow-js/web";

function Dynamic<C extends DynamicTarget>(props: DynamicProps<C>): () => unknown
```

#### `Portal`

Renders `children` into another part of the document, without a wrapper: the content nodes are inserted directly into the mount element (or its shadow root).

```ts
import { Portal } from "@shadow-js/web";

function Portal(props: PortalProps): null
```

#### `ErrorBoundary`

Renders `children`.

```ts
import { ErrorBoundary } from "@shadow-js/web";

function ErrorBoundary(props: ErrorBoundaryProps): Accessor<unknown>

function ErrorBoundary(props: ErrorBoundaryLooseProps): Accessor<unknown>
```

#### `Suspense`

Shows `fallback` while resources read inside it are loading for the first time.

```ts
import { Suspense } from "@shadow-js/web";

function Suspense(props: SuspenseProps): Accessor<unknown>
```

#### `lazy`

Defines a component whose code is loaded on first render.

```ts
import { lazy } from "@shadow-js/web";

function lazy<P>(
  loader: () => Promise<{ default: ComponentFunction<P> }>,
): LazyComponent<P>
```

### Server rendering

#### `renderToString`

Renders an app to an HTML string synchronously: the simplest server render, for pages whose data is already at hand.

```ts
import { renderToString } from "@shadow-js/web/server";

function renderToString(code: () => unknown, options?: RenderOptions): string
```

## Conventions

- **Read a signal by calling it.** `const [count, setCount] = useStore(0)`; write `count()` where the value is read, and `setCount(5)` or `setCount((n) => n + 1)` to write.
- **A component runs once.** Code in the component body runs when the component is created, not on every update. Put what must react to change inside JSX, `useMemo` or `useEffect`.
- **Use `class`, not `className`.** Attributes follow the DOM: `class`, `onClick`, `onInput`. A `style` takes a string or an object.
- **Lists and conditions are components.** Use `<For each={items()}>` for lists and `<Show when={cond()}>` for conditions, so only the changed branch or row is touched.
- **Clean up what you start.** Call `onCleanup` for timers, listeners and subscriptions. An effect that runs again runs its previous cleanup first.
- **Derived values are memos.** Compute from signals with `useMemo`; a memo must not write signals.
- **Import from the package root.** `import { useStore } from "@shadow-js/core"`, `import { For } from "@shadow-js/web"`. The `/extend` entries are for library authors.

## Common mistakes

### Reading a signal once and expecting updates

A plain `const x = count()` in the component body reads once. Read the signal inside JSX, a memo or an effect.

```tsx
import { useStore } from "@shadow-js/core";

export function Wrong() {
  const [count, setCount] = useStore(0);
  const once = count(); // reads once, never updates
  return <button onClick={() => setCount((n) => n + 1)}>{count()} {once}</button>;
}
```

### Writing a signal inside a memo

A memo computes a value from other values. Writing a signal while it computes throws in development. Move the write to an event handler or an effect.

The runtime reports it as `WRITE_IN_MEMO`.

### Creating an effect outside any owner

`useEffect` called at the top level of a module, outside a component or `createRoot`, has no owner to dispose it. Create it inside a component.

The runtime reports it as `NO_OWNER_EFFECT`.

## Documentation

### Get started

- [Introduction](https://shadowjs.net/introduction/why-shadowjs/): ShadowJS compiles JSX into direct DOM updates. A component runs once, and a change touches only the node that reads it.
- [Quick start](https://shadowjs.net/introduction/quick-start/): Create an app, run it, and change a component.
- [Project structure](https://shadowjs.net/introduction/project-structure/): How a ShadowJS app is laid out on disk, what each file does, and which package provides which part of the framework.
### Learn

- [The mental model](https://shadowjs.net/introduction/mental-model/): A component runs once. What runs again is the small piece of code around each place that reads a signal.
- [Forgiving reactivity](https://shadowjs.net/introduction/forgiving-reactivity/): The exact rules by which the compiler keeps plain constants, destructured props and conditional returns live inside components, each rule proven, and the limits where it stops.
- [State](https://shadowjs.net/learn/state/): State is a value you read by calling it. When it changes, only the code that read it runs again.
- [Derived values](https://shadowjs.net/learn/derived-values/): Compute a value from other values. It runs again only when something it read changes.
- [Effects](https://shadowjs.net/learn/effects/): An effect runs code when something it read changes, and cleans up after itself.
- [Control flow](https://shadowjs.net/learn/control-flow/): Show and Switch decide what is on the page. Only the branch that changes is built or removed.
- [Lists](https://shadowjs.net/learn/lists/): For renders one row per item and moves rows instead of rebuilding them.
- [Context](https://shadowjs.net/guides/context/): Provide values to a subtree with createContext, read them with useContext, make them reactive, override them.
- [Suspense and resources](https://shadowjs.net/guides/suspense-and-resources/): Load async data with useResource, show loading UI with Suspense, keep old data while refetching, handle failures, update optimistically, and understand how the resource cache works with server rendering.
- [Data fetching](https://shadowjs.net/guides/data-fetching/): Load, refetch, mutate and optimistically update server data with useResource and useOptimistic, and choose between resources and router loaders.
- [Error handling](https://shadowjs.net/guides/error-handling/): Catch render, effect and resource errors with ErrorBoundary, route errors with catchError, onError and handleError, and recognize ShadowJS error codes.
- [Transitions](https://shadowjs.net/guides/transitions/): Keep the current screen while the next one loads with startTransition, useTransition and useDeferredValue. How transitions interact with Suspense, what isPending means, and what runs on the server.
- [Styling](https://shadowjs.net/guides/styling/): Style components with class, classList, inline styles and CSS files, and set up Tailwind CSS 4 with the Vite plugin.
### Build a todo app

- [1. Set up the project](https://shadowjs.net/tutorial/01-setup/): Create a ShadowJS app with create-shadow-app, start the dev server and replace the starter page with the first lines of the todo app.
- [2. Write the first component](https://shadowjs.net/tutorial/02-first-component/): Pass props to a component, keep a todo in a store and update one checkbox and one class when it changes.
- [3. Render a list](https://shadowjs.net/tutorial/03-todo-list/): Keep many todos in one store, render them with For and Show, and watch a derived count stay in sync.
- [4. Add todos with a form](https://shadowjs.net/tutorial/04-adding-todos/): Build a controlled form with useStore, handle submit, focus the input on mount and add new todos to the list.
- [5. Filters and derived state](https://shadowjs.net/tutorial/05-filters-and-derived-state/): Show all, active or done todos with a memo, derive counts from the store and clear completed items.
- [6. Persistence and effects](https://shadowjs.net/tutorial/06-persistence-and-effects/): Load todos from localStorage after mount, save them with a deferred effect, sync other tabs and clean up listeners.
- [8. Server render and build](https://shadowjs.net/tutorial/08-server-render-and-build/): Render the todo app to HTML with renderToString, hydrate it in the browser, run it through Vite's SSR hooks and produce a production build.
### Guides

- [State with useStore](https://shadowjs.net/guides/state/): Hold single values and deeply nested data in reactive state, update it in every supported form, and know exactly when the DOM and your effects see the change.
- [Effects and lifecycle](https://shadowjs.net/guides/effects-and-lifecycle/): Run side effects with automatic dependencies, clean them up, control ownership and flush timing, and know what runs on the server.
- [Control flow in depth](https://shadowjs.net/guides/control-flow/): Show, Switch, For, Index, Dynamic, Portal, Fragment and lazy components, what each one rebuilds, and how fallbacks behave.
- [Lists and keys](https://shadowjs.net/guides/lists-and-keys/): Render and update lists with For and Index, choose between item references and keys, keep DOM nodes and row state across updates, and make selection O(1) with useSelector.
- [Server rendering](https://shadowjs.net/guides/ssr-hydration-streaming/): Render components to HTML on the server, hand resource data to the browser, and hydrate the markup without recreating a single node.
- [Accessibility](https://shadowjs.net/guides/accessibility/): Write accessible components with semantic HTML, correct ARIA attribute values, labels, focus management, live regions and keyboard handling.
- [Performance](https://shadowjs.net/guides/performance/): Keep updates proportional to what changed, inspect compiled output, and measure rendering and memory in your application.
- [Bundle size](https://shadowjs.net/guides/bundle-size/): Measure what your bundle ships, what each ShadowJS package costs, and which import patterns and package fields keep it small.
- [Benchmarks](https://shadowjs.net/guides/benchmarks/): The row benchmark behind the numbers on the home page: the app, the machine, the method and every measurement, read from the newest run.
- [Security](https://shadowjs.net/guides/security/): What ShadowJS escapes, blocks and validates for you, what stays your responsibility, and how to handle CSP, environment variables and tokens.
- [Extending ShadowJS](https://shadowjs.net/guides/extending/): Build your own hooks, reactive primitives and control-flow components from the same extension APIs the built-ins use, with the same ownership, scheduling and diagnostics.
- [MDX and Markdown](https://shadowjs.net/guides/mdx/): Write pages in MDX with @shadow-js/vite/mdx, mount them as components, replace Markdown elements, read frontmatter, build a content collection, and know what the plugin does in dev, build and SSR.
- [Dark mode](https://shadowjs.net/guides/dark-mode/): Provide a persisted light and dark theme through context, apply it to the document root, and follow the system preference until the user chooses.
- [Debounced search](https://shadowjs.net/guides/debounced-search/): Build a search box that waits for a pause in typing, aborts superseded requests, and shows loading, empty and error states.
- [Optimistic updates](https://shadowjs.net/guides/optimistic-updates/): Show a new todo the moment it is submitted, confirm it when the real list updates, and roll it back with an error message when the save fails.
- [Infinite list](https://shadowjs.net/guides/infinite-list/): Append pages to a keyed list when a sentinel scrolls into view, without recreating the rows that are already on screen.
- [Sortable table](https://shadowjs.net/guides/data-table/): Build a table that filters with a text input, sorts by clicking a column header, reports the sort with aria-sort, and reorders its existing rows instead of recreating them.
- [Form validation](https://shadowjs.net/guides/form-validation/): Build an accessible signup form with a store for the values, derived error messages, touched state and a submit button that follows validity.
- [Modal dialog](https://shadowjs.net/guides/modal-dialog/): Build an accessible modal dialog with a portal, focus management, Escape and backdrop dismissal, and a Tab focus trap.
- [Accessible tabs](https://shadowjs.net/guides/tabs/): Build a tab set with ARIA roles, a roving tabindex, arrow-key navigation with wrap-around, and hidden panels, and test it with keyboard events.
- [Keyboard shortcuts](https://shadowjs.net/guides/keyboard-shortcuts/): Register global keyboard shortcuts with a hook that owns one document listener, matches modifiers exactly and skips text fields.
- [Undo and redo](https://shadowjs.net/guides/undo-redo/): Add undo and redo to a store-backed document by pushing snapshot() copies onto a capped history and restoring them with reconcile(), so unchanged items keep their identity and their DOM rows.
### How it works

- [Reactivity model](https://shadowjs.net/concepts/reactivity-model/): Signals, memos, effects, derived constants and stores as one dependency graph, and the exact rules for what is tracked.
- [Ownership](https://shadowjs.net/concepts/ownership/): How effects, memos, cleanups and errors belong to an owner tree, in which order disposal runs, and what the NO_OWNER diagnostics mean.
- [Scheduler and timing](https://shadowjs.net/concepts/scheduler/): Exactly when signal and store writes reach effects and the DOM, in which order effects run, and what stops an effect loop.
- [Fine-grained rendering](https://shadowjs.net/concepts/fine-grained-rendering/): What the compiler turns JSX into (template, insert, effect, delegated events), why no virtual DOM exists, and why the live DOM contains no comment nodes.
- [Reconciliation](https://shadowjs.net/concepts/reconciliation/): How arrays given to insert and For are reconciled against the DOM, which nodes move and which are created, and how keyed and unkeyed lists differ.
- [Hydration keys](https://shadowjs.net/concepts/hydration-keys/): The deterministic key algorithm that lets the client claim server-rendered DOM, the region markers in server HTML, resource keys, and why keys never depend on owners, effects or memos.
### Tooling

- [The compiler](https://shadowjs.net/guides/compiler/): Read what compile() emits, know which literal and root rewrites it applies, write compiler plugins that run in the build, and pass them to the Vite plugin.
- [The Vite plugin](https://shadowjs.net/guides/vite/): Add @shadow-js/vite to a Vite app, know which files it compiles and which it skips, how dev, build, SSR and Vitest differ, what its options do, and how compile errors and bad options are reported.
- [create-shadow-app](https://shadowjs.net/guides/create-shadow-app/): Scaffold a ShadowJS app with npm create shadow-app - the flags, the spa and dusk templates, the add-ons, the package manager choice, adding features later, upgrading, and the fixes for the usual failures.
- [Configuration](https://shadowjs.net/reference/configuration/): Compiler, Vite and TypeScript options for a ShadowJS app, with defaults and the effect of each setting.
### Migrate

- [Choose a path](https://shadowjs.net/migrate/): Choose a migration path for your React, React Router, Preact, Next.js, Solid, shadcn/ui or Tailwind app, then follow an incremental order that keeps the old app running.
- [From React](https://shadowjs.net/migrate/react/): Port React components, hooks, context, forms and data fetching to ShadowJS, with application examples and API mappings.
- [From React Router](https://shadowjs.net/migrate/react-router/): Port React Router and Remix navigation to @shadow-js/router with a codemod for links, the outlet, redirects and the navigation hooks, and a map from route objects, loaders and the router provider to defineRoute and createRouter.
- [From Preact](https://shadowjs.net/migrate/preact/): Port Preact components, hooks and signals to ShadowJS with a codemod for the hooks and notes for render, class components, @preact/signals and server rendering.
- [From Next.js](https://shadowjs.net/migrate/nextjs/): Move a Next.js app to Dusk with file routes and loaders, server functions for actions, API routes for route handlers, middleware, head tags, environment variables, Clerk and Convex, and read what has no equivalent yet.
- [From Solid](https://shadowjs.net/migrate/solid/): Map every Solid, Solid Router and SolidStart API to its ShadowJS equivalent, with proven before and after code and an exact list of what differs or does not exist yet.
- [From Socket.IO](https://shadowjs.net/migrate/socket-io/): Port a Socket.io server and client to channels, rules, acknowledgments, presence and resumable delivery in @shadow-js/realtime, with a map from rooms, namespaces, middleware, acks and the Redis adapter, and what Socket.io does that this package does not.
- [From shadcn/ui and Radix](https://shadowjs.net/migrate/shadcn-radix/): Port shadcn/ui components to ShadowJS on top of @shadow-js/primitives, with the Radix concept mapping, the parts the library does not have yet, and keyboard and ARIA behavior.
- [From Tailwind](https://shadowjs.net/migrate/tailwind/): Set up Tailwind CSS v4 the way the create-shadow-app template does, write static and dynamic classes that Tailwind can see, build variants without a dependency, and move a React or Next.js Tailwind setup over.
### Reference

- [Overview](https://shadowjs.net/reference/): Every exported function, type and class of the packages this site documents, from the TSDoc of their entry points.
- [Diagnostics](https://shadowjs.net/reference/diagnostics/): Every diagnostic code ShadowJS raises in development, what it means, how to trigger it, and how to fix it.
- [@shadow-js/core](https://shadowjs.net/reference/core/): The 82 exports of @shadow-js/core: 41 functions, 1 class, 23 interfaces, 13 types and 4 consts.
- [@shadow-js/core/extend](https://shadowjs.net/reference/core-extend/): The 71 exports of @shadow-js/core/extend: 45 functions, 4 classes, 11 interfaces, 8 types, 2 consts and 1 component.
- [@shadow-js/web](https://shadowjs.net/reference/web/): The 119 exports of @shadow-js/web: 36 functions, 35 interfaces, 27 types, 20 components and 1 namespace.
- [@shadow-js/web/extend](https://shadowjs.net/reference/web-extend/): The 32 exports of @shadow-js/web/extend: 19 functions, 6 interfaces, 1 type and 6 consts.
- [@shadow-js/web/server](https://shadowjs.net/reference/web-server/): The 33 exports of @shadow-js/web/server: 23 functions, 9 interfaces and 1 component.
- [@shadow-js/router](https://shadowjs.net/reference/router/): The 77 exports of @shadow-js/router: 25 functions, 1 class, 34 interfaces, 13 types and 4 components.
- [@shadow-js/compiler](https://shadowjs.net/reference/compiler/): The 42 exports of @shadow-js/compiler: 4 functions, 1 class, 33 interfaces and 4 types.
- [@shadow-js/vite](https://shadowjs.net/reference/vite/): The 3 exports of @shadow-js/vite: 2 functions and 1 interface.
- [create-shadow-app](https://shadowjs.net/reference/create-shadow-app/): The 47 exports of create-shadow-app: 22 functions, 1 class, 11 interfaces, 5 types and 8 consts.

Every documentation page is also available as Markdown: add `index.md` to its address.

## Reference discovery

This site documents ShadowJS 1.0.0. The [complete page index](https://shadowjs.net/agent-index.json) includes every task, API symbol and Markdown URL. Read the relevant pages as needed; avoid loading the full catalog into context.
