A store (aka state management) is a tool that helps you manage complex UI state.
Not properly managing UI state is one of the most common causes of buggy user interfaces. A store enables you to get even the most complex UI state logic under control.
A store works by representing state changes as atomic changes to an immutable data structure, enabling a fundamentally more robust state management.
We recommend using Vike extensions for automatically integrating state management tools.
You can also manually integrate any state management tool you want.
Manual integration
ðŸ§
Instead of manually integrating your store, we generally recommend using a Vike extension instead.
👉
We recommend using this advanced capability — which can be complex — only if you have a clear reason why alternatives aren't an option for you.
When using a store with SSR, the initial state of the store is determined on the server side (during SSR) and then passed to the client side.
You must ensure that the store's initial state is exactly the same on both the client- and the server-side (otherwise, you'll get a hydration mismatch).
The integration can be broken down into three steps:
// pages/+onCreatePageContext.ssr.js// Environment: serverimport { createStore } from 'awesome-store'// Note how we use `.ssr.js` instead of `.server.js` — this hook only runs during SSR,// not during client-side navigation (`pageContext.json` requests).export function onCreatePageContext(pageContext) { pageContext.store = createStore()}
// pages/+onCreatePageContext.ssr.ts// Environment: serverimport { createStore } from 'awesome-store'import type { PageContextServer } from 'vike/types'// Note how we use `.ssr.js` instead of `.server.js` — this hook only runs during SSR,// not during client-side navigation (`pageContext.json` requests).export function onCreatePageContext(pageContext: PageContextServer) { pageContext.store = createStore()}
// pages/+onAfterRenderHtml.js// Environment: serverexport function onAfterRenderHtml(pageContext) { pageContext.storeInitialState = pageContext.store.getState()}
// pages/+onAfterRenderHtml.ts// Environment: serverimport type { PageContextServer } from 'vike/types'export function onAfterRenderHtml(pageContext: PageContextServer) { pageContext.storeInitialState = pageContext.store.getState()}
If you use React or Solid, you may need to use +Wrapper:
// pages/+Wrapper.jsx// Environment: server, clientimport React from 'react'import { Provider } from 'awesome-store/react'import { usePageContext } from 'vike-react/usePageContext'export default function StoreProvider({ children }) { const pageContext = usePageContext() return <Provider store={pageContext.store}>{children}</Provider>}
// pages/+Wrapper.tsx// Environment: server, clientimport React from 'react'import { Provider } from 'awesome-store/react'import { usePageContext } from 'vike-react/usePageContext'export default function StoreProvider({ children }: { children: React.ReactNode }) { const pageContext = usePageContext() return <Provider store={pageContext.store}>{children}</Provider>}
// pages/+config.tsimport type { Config } from 'vike/types'export default { passToClient: ['storeInitialState']} satisfies Config
3. Hydration
On the client-side, initialize the store with pageContext.storeInitialState upon hydration, for example at +onBeforeRenderClient:
// pages/+onBeforeRenderClient.js// Environment: clientimport { createStore } from 'awesome-store'export function onBeforeRenderClient(pageContext) { if (pageContext.isHydration) { // Hydration. We must use the same state than on the server-side. pageContext.globalContext.store = createStore(pageContext.storeInitialState) } else { // Client-side navigation. Nothing to do: the store was already initialized at hydration. assert(pageContext.globalContext.store) }}
// pages/+onBeforeRenderClient.ts// Environment: clientimport type { PageContextClient } from 'vike/types'import { createStore } from 'awesome-store'export function onBeforeRenderClient(pageContext: PageContextClient) { if (pageContext.isHydration) { // Hydration. We must use the same state than on the server-side. pageContext.globalContext.store = createStore(pageContext.storeInitialState) } else { // Client-side navigation. Nothing to do: the store was already initialized at hydration. assert(pageContext.globalContext.store) }}