State Management
There are many different ways to store and manage state in a Solid Start app. In this section we will discuss some of the most common ways to manage state in a project.
What is state?
State is any information that is stored in your application. This can be anything from the current user's name to the current page the user is on. State can be stored in many different places, and can be accessed in many different ways.
Client-side state
Client-side state is any state that is stored on the client's browser (and therefore, not available on the server). This can be stored in a variety of ways, including:
- Local storage
- Session storage
- Cookies
- Query parameters
- Client-side stores
Local storage
Local storage is a way to store data in the browser. This data is stored in the browser's local storage, and can be accessed by any page on the same domain. This data is persistent, and will remain even if the user closes their browser.
When you need to access the localStorage
API, you can either use a createEffect
hook or a createRouteAction
which gaurantees that the code will run in the browser.
components/ThemePicker.tsx
tsx
import { createEffectCreates a reactive computation that runs after the render phase
```typescript
export function createEffect<T>(
fn: (v: T) => T,
value?: T,
options?: { name?: string }
): void;
```
(alias) function createEffect<Next>(fn: EffectFunction<undefined | NoInfer<Next>, Next>): void (+1 overload)
import createEffect
, createSignalCreates a simple reactive state with a getter and setter
```typescript
const [state: Accessor<T>, setState: Setter<T>] = createSignal<T>(
value: T,
options?: { name?: string, equals?: false | ((prev: T, next: T) => boolean) }
)
```
(alias) function createSignal<T>(): Signal<T | undefined> (+1 overload)
import createSignal
} from "solid-js";
export function ButtonThemeCacherfunction ButtonThemeCacher(): JSX.Element
() { const [themeconst theme: Accessor<string>
, setThemeconst setTheme: Setter<string>
] = createSignalCreates a simple reactive state with a getter and setter
```typescript
const [state: Accessor<T>, setState: Setter<T>] = createSignal<T>(
value: T,
options?: { name?: string, equals?: false | ((prev: T, next: T) => boolean) }
)
```
(alias) createSignal<string>(value: string, options?: SignalOptions<string> | undefined): Signal<string> (+1 overload)
import createSignal
(LIGHT);
function handleClick(local function) handleClick(): void
() { setThemeconst setTheme: <"light" | "dark">(value: "light" | "dark") => "light" | "dark" (+3 overloads)
(themeconst theme: () => string
() === LIGHT ? DARK : LIGHT); }
createEffectCreates a reactive computation that runs after the render phase
```typescript
export function createEffect<T>(
fn: (v: T) => T,
value?: T,
options?: { name?: string }
): void;
```
(alias) createEffect<void>(fn: EffectFunction<void | undefined, void>): void (+1 overload)
import createEffect
(() => { localStorage[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/localStorage)
var localStorage: Storage
.setItemSets the value of the pair identified by key to value, creating a new key/value pair if none existed for key previously.
Throws a "QuotaExceededError" DOMException exception if the new value couldn't be set. (Setting could fail if, e.g., the user has disabled storage for the site, or if the quota has been exceeded.)
Dispatches a storage event on Window objects holding an equivalent Storage object.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Storage/setItem)
(method) Storage.setItem(key: string, value: string): void
("theme", themeconst theme: () => string
()); });
return (
<button(property) JSX.HTMLElementTags.button: JSX.ButtonHTMLAttributes<HTMLButtonElement>
onClick(property) JSX.CustomEventHandlersCamelCase<HTMLButtonElement>.onClick?: JSX.EventHandlerUnion<HTMLButtonElement, MouseEvent> | undefined
={handleClick(local function) handleClick(): void
} type(property) JSX.ButtonHTMLAttributes<HTMLButtonElement>.type?: "button" | "submit" | "reset" | undefined
="button"> Toggle theme
</button(property) JSX.HTMLElementTags.button: JSX.ButtonHTMLAttributes<HTMLButtonElement>
> );
}
components/ThemePicker.tsx
tsx
import { createEffectCreates a reactive computation that runs after the render phase
```typescript
export function createEffect<T>(
fn: (v: T) => T,
value?: T,
options?: { name?: string }
): void;
```
(alias) function createEffect<Next>(fn: EffectFunction<undefined | NoInfer<Next>, Next>): void (+1 overload)
import createEffect
, createSignalCreates a simple reactive state with a getter and setter
```typescript
const [state: Accessor<T>, setState: Setter<T>] = createSignal<T>(
value: T,
options?: { name?: string, equals?: false | ((prev: T, next: T) => boolean) }
)
```
(alias) function createSignal<T>(): Signal<T | undefined> (+1 overload)
import createSignal
} from "solid-js";
export function ButtonThemeCacherfunction ButtonThemeCacher(): JSX.Element
() { const [themeconst theme: Accessor<string>
, setThemeconst setTheme: Setter<string>
] = createSignalCreates a simple reactive state with a getter and setter
```typescript
const [state: Accessor<T>, setState: Setter<T>] = createSignal<T>(
value: T,
options?: { name?: string, equals?: false | ((prev: T, next: T) => boolean) }
)
```
(alias) createSignal<string>(value: string, options?: SignalOptions<string> | undefined): Signal<string> (+1 overload)
import createSignal
(LIGHT);
function handleClick(local function) handleClick(): void
() { setThemeconst setTheme: <"light" | "dark">(value: "light" | "dark") => "light" | "dark" (+3 overloads)
(themeconst theme: () => string
() === LIGHT ? DARK : LIGHT); }
createEffectCreates a reactive computation that runs after the render phase
```typescript
export function createEffect<T>(
fn: (v: T) => T,
value?: T,
options?: { name?: string }
): void;
```
(alias) createEffect<void>(fn: EffectFunction<void | undefined, void>): void (+1 overload)
import createEffect
(() => { localStorage[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/localStorage)
var localStorage: Storage
.setItemSets the value of the pair identified by key to value, creating a new key/value pair if none existed for key previously.
Throws a "QuotaExceededError" DOMException exception if the new value couldn't be set. (Setting could fail if, e.g., the user has disabled storage for the site, or if the quota has been exceeded.)
Dispatches a storage event on Window objects holding an equivalent Storage object.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Storage/setItem)
(method) Storage.setItem(key: string, value: string): void
("theme", themeconst theme: () => string
()); });
return (
<button(property) JSX.HTMLElementTags.button: JSX.ButtonHTMLAttributes<HTMLButtonElement>
onClick(property) JSX.CustomEventHandlersCamelCase<HTMLButtonElement>.onClick?: JSX.EventHandlerUnion<HTMLButtonElement, MouseEvent> | undefined
={handleClick(local function) handleClick(): void
} type(property) JSX.ButtonHTMLAttributes<HTMLButtonElement>.type?: "button" | "submit" | "reset" | undefined
="button"> Toggle theme
</button(property) JSX.HTMLElementTags.button: JSX.ButtonHTMLAttributes<HTMLButtonElement>
> );
}
Session storage
TBD...
Cookies
TBD...
Query parameters
TBD...
Client-side stores
TBD...
Server-side state
Server-side state is any state that is stored on the server. This can be stored in a variety of ways, including:
- Databases
- Session cookies
- Server-side stores
Databases
TBD...
Session cookies
TBD...
Server-side stores
TBD...
Global state and the server
While it is possible to use global state and computations, Context is sometimes a better solution. Additionally, it is important to note that global state should not be used in SSR (server side rendering) solutions, such as Solid Start.
On the server, global state is shared across requests, and the lack of data isolation can (and will) lead to bugs, memory leaks and has security implications. It is recommended that application state should always be provided via Context
instead of relying on global.