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
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ A lightweight, modular, and performant state management ecosystem for building m
| [`@nano_kit/platform-web`](packages/platform-web#readme) | Web platform adapters and reactive helpers. | [![NPM version][platform-web-npm]][platform-web-npm-url] |
| [`@nano_kit/router`](packages/router#readme) | Routing library, built on @nano_kit/store. | [![NPM version][router-npm]][router-npm-url] |
| [`@nano_kit/query`](packages/query#readme) | Data fetching and caching library, built on @nano_kit/store. | [![NPM version][query-npm]][query-npm-url] |
| [`@nano_kit/cookie-store`](packages/cookie-store#readme) | CookieStore-compatible implementation for SSR and testing environments. | [![NPM version][cookie-store-npm]][cookie-store-npm-url] |
| [`@nano_kit/ssr`](packages/ssr#readme) | Base package for server-side rendering capabilities. | [![NPM version][ssr-npm]][ssr-npm-url] |
| [`@nano_kit/react`](packages/react#readme) | React integration for @nano_kit/store. | [![NPM version][react-npm]][react-npm-url] |
| [`@nano_kit/react-router`](packages/react-router#readme) | React integration for @nano_kit/router. | [![NPM version][react-router-npm]][react-router-npm-url] |
Expand All @@ -24,6 +23,7 @@ A lightweight, modular, and performant state management ecosystem for building m
| [`@nano_kit/svelte`](packages/svelte#readme) | Svelte integration for @nano_kit/store. | [![NPM version][svelte-npm]][svelte-npm-url] |
| [`@nano_kit/svelte-router`](packages/svelte-router#readme) | Svelte integration for @nano_kit/router. | [![NPM version][svelte-router-npm]][svelte-router-npm-url] |
| [`@nano_kit/svelte-ssr`](packages/svelte-ssr#readme) | Svelte adapter for server-side rendering capabilities. | [![NPM version][svelte-ssr-npm]][svelte-ssr-npm-url] |
| [`@nano_kit/svelte-kit`](packages/svelte-kit#readme) | SvelteKit integration for stores, router, and SSR. | [![NPM version][svelte-kit-npm]][svelte-kit-npm-url] |
| [`@nano_kit/next-router`](packages/next-router#readme) | Next.js integration for @nano_kit/router. | [![NPM version][next-router-npm]][next-router-npm-url] |

<!-- nanoviews -->
Expand Down Expand Up @@ -61,11 +61,6 @@ A lightweight, modular, and performant state management ecosystem for building m
[query-npm]: https://img.shields.io/npm/v/%40nano_kit%2Fquery.svg
[query-npm-url]: https://npmjs.com/package/@nano_kit/query

<!-- cookie-store -->

[cookie-store-npm]: https://img.shields.io/npm/v/%40nano_kit%2Fcookie-store.svg
[cookie-store-npm-url]: https://npmjs.com/package/@nano_kit/cookie-store

<!-- ssr -->

[ssr-npm]: https://img.shields.io/npm/v/%40nano_kit%2Fssr.svg
Expand Down Expand Up @@ -116,6 +111,11 @@ A lightweight, modular, and performant state management ecosystem for building m
[svelte-ssr-npm]: https://img.shields.io/npm/v/%40nano_kit%2Fsvelte-ssr.svg
[svelte-ssr-npm-url]: https://npmjs.com/package/@nano_kit/svelte-ssr

<!-- svelte-kit -->

[svelte-kit-npm]: https://img.shields.io/npm/v/%40nano_kit%2Fsvelte-kit.svg
[svelte-kit-npm-url]: https://npmjs.com/package/@nano_kit/svelte-kit

<!-- next-router -->

[next-router-npm]: https://img.shields.io/npm/v/%40nano_kit%2Fnext-router.svg
Expand Down
14 changes: 13 additions & 1 deletion website/src/content/docs/examples/rick-and-morty.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { Tabs, TabItem } from '@astrojs/starlight/components'

This example shows a larger encyclopedia app built around routing, data loading, and server-side rendering.

Compare the tabs to see the same application model running with React SSR, Next.js App Router, and Next.js Pages Router.
Compare the tabs to see the same application model running with React SSR, Svelte SSR, SvelteKit, Next.js App Router, and Next.js Pages Router.

<Tabs syncKey='example-view'>
<TabItem label='React SSR' icon='seti:react'>
Expand All @@ -36,6 +36,18 @@ Compare the tabs to see the same application model running with React SSR, Next.

[View source on GitHub](https://github.com/TrigenSoftware/nano_kit/tree/main/examples/rick-and-morty/svelte-nano_kit-ssr)
</TabItem>
<TabItem label='SvelteKit' icon='seti:svelte'>
<iframe
src='https://stackblitz.com/github/TrigenSoftware/nano_kit/tree/main/examples/rick-and-morty/svelte-kit-nano_kit-ssr?embed=1&file=src%2Froutes%2F%2Blayout.svelte&view=both'
loading='lazy'
style='width: 100%; height: 500px; border: 0; border-radius: 4px; overflow: hidden;'
title='Rick and Morty Encyclopedia built with SvelteKit and Nano Kit'
allow='accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking'
sandbox='allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts'
/>

[View source on GitHub](https://github.com/TrigenSoftware/nano_kit/tree/main/examples/rick-and-morty/svelte-kit-nano_kit-ssr)
</TabItem>
<TabItem label='Next.js App Router' icon='vercel'>
<iframe
src='https://codesandbox.io/p/sandbox/github/TrigenSoftware/nano_kit/tree/main/examples/rick-and-morty/next-app-nano_kit-ssr?embed=1&file=%2Fsrc%2Fapp%2Flayout.tsx'
Expand Down
38 changes: 27 additions & 11 deletions website/src/content/docs/examples/session.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,37 @@ sidebar:
order: 6
---

import { Aside } from '@astrojs/starlight/components'
import { Aside, Tabs, TabItem } from '@astrojs/starlight/components'

This example shows a small React SSR app that keeps a username in a `session` cookie.
This example shows a small SSR app that keeps a username in a `session` cookie.

It demonstrates request-bound cookie state, `cookieStored`, SSR rendering from incoming cookies, server-side cookie deletion on `/logout`, redirect handling, and hydration without mismatches.

<Aside>StackBlitz runs this example in a sandbox with a fake cookie implementation, so session behavior can be slightly inaccurate there.</Aside>

<iframe
src='https://stackblitz.com/github/TrigenSoftware/nano_kit/tree/main/examples/session/react-nano_kit-ssr?embed=1&file=src%2Findex.tsx&view=both'
loading='lazy'
style='width: 100%; height: 500px; border: 0; border-radius: 4px; overflow: hidden;'
title='Session Cookies built with React SSR and Nano Kit'
allow='accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking'
sandbox='allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts'
/>
<Tabs syncKey='example-view'>
<TabItem label='React SSR' icon='seti:react'>
<iframe
src='https://stackblitz.com/github/TrigenSoftware/nano_kit/tree/main/examples/session/react-nano_kit-ssr?embed=1&file=src%2Findex.tsx&view=both'
loading='lazy'
style='width: 100%; height: 500px; border: 0; border-radius: 4px; overflow: hidden;'
title='Session Cookies built with React SSR and Nano Kit'
allow='accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking'
sandbox='allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts'
/>

[View source on GitHub](https://github.com/TrigenSoftware/nano_kit/tree/main/examples/session/react-nano_kit-ssr)
[View source on GitHub](https://github.com/TrigenSoftware/nano_kit/tree/main/examples/session/react-nano_kit-ssr)
</TabItem>
<TabItem label='SvelteKit' icon='seti:svelte'>
<iframe
src='https://stackblitz.com/github/TrigenSoftware/nano_kit/tree/main/examples/session/svelte-kit-nano_kit-ssr?embed=1&file=src%2Froutes%2F%2Blayout.svelte&view=both'
loading='lazy'
style='width: 100%; height: 500px; border: 0; border-radius: 4px; overflow: hidden;'
title='Session Cookies built with SvelteKit and Nano Kit'
allow='accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking'
sandbox='allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts'
/>

[View source on GitHub](https://github.com/TrigenSoftware/nano_kit/tree/main/examples/session/svelte-kit-nano_kit-ssr)
</TabItem>
</Tabs>
1 change: 1 addition & 0 deletions website/src/content/docs/getting-started/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ unsub()
| [@nano_kit/svelte](/integrations/svelte) | Svelte integration for @nano_kit/store. |
| [@nano_kit/svelte-router](/integrations/svelte-router) | Svelte integration for @nano_kit/router. |
| [@nano_kit/svelte-ssr](/integrations/svelte-ssr) | Svelte SSR adapter for @nano_kit/ssr. |
| [@nano_kit/svelte-kit](/integrations/svelte-kit) | SvelteKit integration for stores, router, and SSR. |
| [@nano_kit/next-router](/integrations/next-router) | Next.js integration for @nano_kit/router. |

## Motivation
Expand Down
1 change: 1 addition & 0 deletions website/src/content/docs/integrations/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ Nano Kit keeps its core framework-agnostic, then adds thin adapters for renderin
| [@nano_kit/svelte](/integrations/svelte) | Svelte integration for @nano_kit/store. |
| [@nano_kit/svelte-router](/integrations/svelte-router) | Svelte integration for @nano_kit/router. |
| [@nano_kit/svelte-ssr](/integrations/svelte-ssr) | Svelte SSR adapter for @nano_kit/ssr. |
| [@nano_kit/svelte-kit](/integrations/svelte-kit) | SvelteKit integration for stores, router, and SSR. |
| [@nano_kit/next-router](/integrations/next-router) | Next.js integration for @nano_kit/router. |
30 changes: 19 additions & 11 deletions website/src/content/docs/integrations/next-router.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: Next.js Router
description: "Integrate @nano_kit/router with Next.js using @nano_kit/next-router."
sidebar:
order: 12
order: 13
---

import { Tabs, TabItem, Code } from '@astrojs/starlight/components'
Expand Down Expand Up @@ -132,19 +132,23 @@ export default function App({ Component, pageProps }: AppProps) {
}
```

For SSR data loading in `getServerSideProps`, use `virtualNavigationContext` together with `dehydrate`.
For SSR data loading in `getServerSideProps`, create a virtual navigation for the current URL and pass its providers to `dehydrate`.

```tsx
import type { GetServerSideProps } from 'next'
import { dehydrate } from '@nano_kit/store'
import { virtualNavigationContext } from '@nano_kit/next-router'
import { dehydrate, provide } from '@nano_kit/store'
import { Location$, Navigation$, virtualNavigation } from '@nano_kit/next-router'
import { routes } from '@/stores/router'
import CharactersPage, { Stores$ } from '@/ui/pages/Characters'

export const getServerSideProps: GetServerSideProps = async (context) => {
const [$location, navigation] = virtualNavigation(context.resolvedUrl, routes)
const dehydrated = await dehydrate(
Stores$,
virtualNavigationContext(context.resolvedUrl, routes)
[
provide(Location$, $location),
provide(Navigation$, navigation)
]
)

return {
Expand Down Expand Up @@ -228,21 +232,25 @@ These helpers are for the Pages Router.

```tsx
import type { GetServerSideProps } from 'next'
import { contextDehydrate } from '@nano_kit/store'
import { InjectionContext, dehydrate, provide } from '@nano_kit/store'
import {
Location$,
Navigation$,
notFound,
redirect,
virtualNavigationContext
virtualNavigation
} from '@nano_kit/next-router'
import { routes } from '@/stores/router'
import { User$ } from '@/stores/user'
import UserPage, { Stores$ } from '@/ui/pages/User'

export const getServerSideProps: GetServerSideProps = async (nextContext) => {
const [context, dehydrated] = await contextDehydrate(
Stores$,
virtualNavigationContext(nextContext.resolvedUrl, routes)
)
const [$location, navigation] = virtualNavigation(nextContext.resolvedUrl, routes)
const context = new InjectionContext([
provide(Location$, $location),
provide(Navigation$, navigation)
])
const dehydrated = await dehydrate(Stores$, context)
const { $user } = context.get(User$)

return notFound($user()) ?? redirect(context) ?? {
Expand Down
39 changes: 26 additions & 13 deletions website/src/content/docs/integrations/next.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: Next.js
description: "Use Nano Kit with Next.js via @nano_kit/react and @nano_kit/next-router."
sidebar:
order: 11
order: 12
---

import { Tabs, TabItem, Code } from '@astrojs/starlight/components'
Expand Down Expand Up @@ -168,7 +168,7 @@ If you do not use `@nano_kit/router`, render `StaticDehydration` directly withou
In the Pages Router, the usual setup is:

- [`HydrationProvider`](/integrations/react#hydrationprovider) in `_app.tsx`
- [`dehydrate()`](/store/ssr#server-side-dehydration) or [`contextDehydrate()`](/store/ssr#server-side-dehydration) in `getServerSideProps`
- [`dehydrate()`](/store/ssr#server-side-dehydration) in `getServerSideProps`
- [`NextNavigationProvider`](/integrations/next-router#nextnavigationprovider) when the app uses `@nano_kit/router`

### `_app.tsx`
Expand Down Expand Up @@ -203,19 +203,23 @@ export default function App({ Component, pageProps }: AppProps) {

When you need server-prefetched store data, dehydrate stores in `getServerSideProps` and pass the snapshot through `pageProps.dehydrated`.

If the page uses `@nano_kit/router`, create the dehydration context with [`virtualNavigationContext`](/integrations/next-router#pages-router).
If the page uses `@nano_kit/router`, create a virtual navigation for the current URL and pass its providers as the second `dehydrate` argument.

```tsx
import type { GetServerSideProps } from 'next'
import { dehydrate } from '@nano_kit/store'
import { virtualNavigationContext } from '@nano_kit/next-router'
import { dehydrate, provide } from '@nano_kit/store'
import { Location$, Navigation$, virtualNavigation } from '@nano_kit/next-router'
import { routes } from '@/stores/router'
import CharactersPage, { Stores$ } from '@/ui/pages/Characters'

export const getServerSideProps: GetServerSideProps = async (context) => {
const [$location, navigation] = virtualNavigation(context.resolvedUrl, routes)
const dehydrated = await dehydrate(
Stores$,
virtualNavigationContext(context.resolvedUrl, routes)
[
provide(Location$, $location),
provide(Navigation$, navigation)
]
)

return {
Expand All @@ -234,17 +238,26 @@ If you want the Pages Router to behave like static dehydration in the App Router

```tsx
import type { GetServerSideProps } from 'next'
import { dehydrate } from '@nano_kit/store'
import { dehydrate, provide } from '@nano_kit/store'
import { isFlight } from '@nano_kit/react'
import { virtualNavigationContext } from '@nano_kit/next-router'
import { Location$, Navigation$, virtualNavigation } from '@nano_kit/next-router'
import { routes } from '@/stores/router'
import CharactersPage, { Stores$ } from '@/ui/pages/Characters'

export const getServerSideProps: GetServerSideProps = async (context) => {
const dehydrated = !isFlight(context.req.headers) && await dehydrate(
Stores$,
virtualNavigationContext(context.resolvedUrl, routes)
)
let dehydrated

if (!isFlight(context.req.headers)) {
const [$location, navigation] = virtualNavigation(context.resolvedUrl, routes)

dehydrated = await dehydrate(
Stores$,
[
provide(Location$, $location),
provide(Navigation$, navigation)
]
)
}

return {
props: {
Expand All @@ -258,7 +271,7 @@ export default function Page() {
}
```

Without `@nano_kit/router`, call `dehydrate(Stores$)` without `virtualNavigationContext`.
Without `@nano_kit/router`, call `dehydrate(Stores$)` without navigation providers.

## Which Package Does What?

Expand Down
36 changes: 32 additions & 4 deletions website/src/content/docs/integrations/preact-ssr.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,6 @@ import { Steps, Tabs, TabItem, Code, Aside } from '@astrojs/starlight/components
- **`vite dev`** — starts the development server with SSR rendering handled in-process.
- **`vite build`** — produces `dist/client/` (browser assets) and `dist/renderer/` (SSR renderer bundle).

Add `cookieStore: true` when SSR stores need access to request cookies. See [SSR Cookies](/ssr/cookies) for the full setup.

3. **Write your production HTTP server**

For production, write your own HTTP server that imports the built renderer and calls `renderer.render(url)` for every incoming request:
Expand All @@ -88,7 +86,10 @@ import { Steps, Tabs, TabItem, Code, Aside } from '@astrojs/starlight/components

// Express example
app.get('*', async (req, res) => {
const result = await renderer.render(req.url, req.headers.cookie)
const result = await renderer.render(req.url, {
cookie: req.headers.cookie,
acceptLanguage: req.headers['accept-language']
})

if (result.setCookieHeaders) {
res.setHeader('Set-Cookie', result.setCookieHeaders)
Expand All @@ -108,6 +109,32 @@ import { Steps, Tabs, TabItem, Code, Aside } from '@astrojs/starlight/components

</Steps>

## SSR Options

The SSR plugin can provide request-bound browser-like dependencies for stores that run during server rendering.

### `cookieStore`

Use `cookieStore: true` when the renderer should provide a request-bound `CookieStore$` during SSR. This feature uses the optional peer package `@nano_kit/platform-web`, so install it in the app before enabling the option. See [SSR Cookies](/ssr/cookies) for the full setup, including store code and `Set-Cookie` forwarding.

```js
ssr({
index: 'src/index.tsx',
cookieStore: true
})
```

### `browserLocale`

Use `browserLocale: true` when the renderer should provide request-bound `Locales$` during SSR. This feature uses the optional peer package `@nano_kit/platform-web`, so install it in the app before enabling the option. The renderer parses the incoming `Accept-Language` header with [`parseLocales`](/platform/web/#locale), so universal stores can inject `Locales$` and resolve the same locale shape on the server and in the browser. See [SSR Locale](/ssr/locale) for the full setup.

```js
ssr({
index: 'src/index.tsx',
browserLocale: true
})
```

## Custom Renderer

To customize the HTML output, extend `PreactRenderer` and override `renderToString`:
Expand Down Expand Up @@ -178,6 +205,7 @@ ssr({
## See Also

- [SSR — Getting Started](/ssr) — how the base SSR package works
- [SSR Cookies](/ssr/cookies) — request-bound cookies, `cookieStore: true`, and `Set-Cookie` forwarding
- [SSR Cookies](/ssr/cookies) — request-bound cookies and `Set-Cookie` forwarding
- [SSR Locale](/ssr/locale) — request-bound locales and `Accept-Language` parsing
- [Router SSR](/router/ssr) — how to add [`Stores$`](/router/ssr#stores-data-dehydration) and [`Head$`](/router/ssr#head-management) to page modules
- [Preact Router](/integrations/preact-router) — [`App`](/integrations/preact-router#app), [`browserNavigation`](/integrations/preact-router#setup), [`router`](/integrations/preact-router#router), and other Preact router APIs
Loading