# atom.io/web

Source: docs/source/pages/docs/web.mdx
URL: /docs/web

# <low-emphasis>atom.io</low-emphasis>/web

`atom.io/web` provides browser-specific integrations for `atom.io`.

Right now, that means a couple of state-persistence strategies in the form of [atom effects](/docs/atom-io#atom-effects).

## package contents

<table-wrapper>

| Export | Description |
| --- | --- |
| `storageSync` | Sync an atom with a browser storage interface. |
| `searchParamSync` | Sync an atom with a query parameter in the current URL. |

</table-wrapper>

## storageSync

`storageSync` hydrates an atom from browser storage when the atom is created, then writes future changes back to storage.

### sync with local storage
Source: docs/source/exhibits/web/sync-with-local-storage.ts

```ts
import { atom } from "atom.io"
import { storageSync } from "atom.io/web"

export const sidebarOpenAtom = atom<boolean | null>({
	key: `sidebarOpen`,
	default: true,
	effects: [storageSync(localStorage, JSON, `sidebarOpen`)],
})
```

The second parameter is any object with `stringify` and `parse` methods. In many cases, `JSON` is exactly what you want.

When the atom is set to `null`, the stored item is removed.

Because the storage object is passed in directly, this also works with `sessionStorage`:

### sync with session storage
Source: docs/source/exhibits/web/sync-with-session-storage.ts

```ts
import { atom } from "atom.io"
import { storageSync } from "atom.io/web"

export const sidebarOpenForSessionAtom = atom<boolean | null>({
	key: `sidebarOpenForSession`,
	default: true,
	effects: [storageSync(sessionStorage, JSON, `sidebarOpen`)],
})
```

If storage is unavailable, such as during SSR, the effect simply does nothing.

## searchParamSync

`searchParamSync` reads from the current page URL, then keeps one search parameter updated as the atom changes.

### sync with the url
Source: docs/source/exhibits/web/sync-with-the-url.ts

```ts
import { atom } from "atom.io"
import { searchParamSync } from "atom.io/web"

export const selectedTabAtom = atom<string | null>({
	key: `selectedTab`,
	default: `overview`,
	effects: [searchParamSync(JSON, `tab`)],
})
```

If the current URL is `/?tab=%22settings%22`, `URLSearchParams` reads the
parameter value as `"settings"`, and the atom initializes to the JavaScript
string `settings`.

The `%22` sequences are the URL-encoded double quotes from the JSON string. For
cleaner URLs such as `?tab=settings`, pass a string serializer instead, for
example `{ stringify: String, parse: String }`.

Later, if the atom changes to `"billing"`, the URL is updated in place using `history.replaceState`, so the page does not reload.

If the atom is set to `null`, that search parameter is removed from the URL.

`searchParamSync` preserves the rest of the URL, including unrelated query params and the hash fragment.

## serialization

Both effects take the same kind of serializer:

### string interface
Source: docs/source/exhibits/web/string-interface.ts

```ts
type StringInterface<T> = {
	stringify: (t: T) => string
	parse: (s: string) => T
}
```

That means you can use `JSON`, or define your own format if you want cleaner URLs or custom parsing rules.

## ssr behavior

Both effects are safe to include in code that also runs on the server.

- `storageSync` does nothing when the storage object is `undefined`
- `searchParamSync` does nothing when `window`, `location`, or `history` are unavailable

This makes it straightforward to declare one atom and use it in both browser and SSR contexts.
