# astro-react-zustand
> name: astro-react-zustand description: Build and hydrate React 19 islands inside Astro using @astrojs/react, with Zustand stores for shared state. Use when creating/updating React components with client directives, wiring Zustand stores, or debugging SSR/CSR hydration. ---
- Author: HelloChenJL
- Repository: HelloChenJL/jiujiujiang-blog
- Version: 20260206161753
- Stars: 0
- Forks: 0
- Last Updated: 2026-02-06
- Source: https://github.com/HelloChenJL/jiujiujiang-blog
- Web: https://mule.run/skillshub/@@HelloChenJL/jiujiujiang-blog~astro-react-zustand:20260206161753
---
name: astro-react-zustand
description: Build and hydrate React 19 islands inside Astro using @astrojs/react, with Zustand stores for shared state. Use when creating/updating React components with client directives, wiring Zustand stores, or debugging SSR/CSR hydration.
---
# Astro React Zustand
## Overview
Embed React islands in Astro, manage shared state with Zustand, and avoid hydration pitfalls.
## Quick Start
- Integration: keep `@astrojs/react` in `astro.config.mjs` integrations. Use client directives (`client:load|visible|idle|only="react"`) when placing React components in `.astro`.
- Entry point example:
```astro
---
import Counter from '../components/Counter';
---
```
## Zustand Store Pattern
Create stores once per module to avoid multi-instance bugs during SSR.
```ts
// src/stores/useCounter.ts
import { create } from 'zustand';
type CounterState = { count: number; inc: () => void; reset: () => void };
export const useCounter = create((set) => ({
count: 0,
inc: () => set((s) => ({ count: s.count + 1 })),
reset: () => set({ count: 0 }),
}));
```
Use selectors to limit re-renders:
```ts
const count = useCounter((s) => s.count);
const inc = useCounter((s) => s.inc);
```
## Hydration & SSR Notes
- Keep store modules free of browser-only APIs; guard with `typeof window !== 'undefined'` inside actions if needed.
- For initial server data, pass props into islands and hydrate store inside `useEffect` to avoid mismatches:
```tsx
useEffect(() => useCounter.setState({ count: initialCount }), [initialCount]);
```
- Prefer `client:visible`/`client:idle` for non-critical UI to reduce bundle cost; use `client:only="react"` for pure React pages.
## Component Template
```tsx
// src/components/Counter.tsx
import { useEffect } from 'react';
import { useCounter } from '../stores/useCounter';
export default function Counter({ initialCount = 0 }) {
const count = useCounter((s) => s.count);
const inc = useCounter((s) => s.inc);
useEffect(() => useCounter.setState({ count: initialCount }), [initialCount]);
return (
);
}
```
## Debugging Checklist
- Hydration warnings: align server/client markup; avoid random values during render (move to `useEffect`).
- Multiple store instances: confirm `create()` is not inside a component.
- TypeScript: export types for store shape; prefer `satisfies` to ensure literal inference for actions/state.