\n\n\n

SSR-гидрация: как работает автоматически

\n

Nuxt передаёт состояние Pinia через useNuxtApp().payload. При SSR-рендере store заполняется на сервере, сериализуется в HTML (в тег <script id=\"__NUXT_DATA__\">), и клиент гидрирует его без повторных запросов. Это работает «из коробки» — вручную ничего сериализовать не нужно.

\n\n

Инициализация store на сервере через plugins

\n
// plugins/pinia-init.server.ts\nexport default defineNuxtPlugin(async () => {\n  const userStore = useUserStore();\n  // Заполняем store данными при каждом SSR-запросе\n  const event = useRequestEvent();\n  const token = getCookie(event, 'auth_token');\n  if (token) {\n    await userStore.fetchUser(/* decode token -> id */);\n  }\n});\n
\n\n

Persist состояния между сессиями (localStorage)

\n
npm install @pinia-plugin-persistedstate/nuxt
\n
// nuxt.config.ts\nexport default defineNuxtConfig({\n  modules: ['@pinia/nuxt', '@pinia-plugin-persistedstate/nuxt'],\n});\n
\n
// stores/cart.ts\nexport const useCartStore = defineStore('cart', () => {\n  const items = ref([]);\n  return { items };\n}, {\n  persist: {\n    storage: piniaPluginPersistedstate.localStorage(),\n    // На сервере localStorage недоступен — плагин автоматически пропускает\n  },\n});\n
\n\n

Подводные камни

\n
    \n
  • Singleton store на сервере: при SSR каждый запрос должен получать свой экземпляр Pinia. @pinia/nuxt создаёт его через useNuxtApp().$pinia — не обращайтесь к store вне контекста Nuxt-плагина или composable.
  • \n
  • localStorage на сервере: localStorage недоступен при SSR. Если используете persist, убедитесь что плагин поддерживает SSR-режим, иначе получите ReferenceError.
  • \n
  • useStore вне setup(): вызов store за пределами setup() или Nuxt-плагина вызовет ошибку «no active Pinia». Инициализируйте store только внутри composable-функций.
  • \n
  • Дублирование fetch: если store инициализируется в плагине на сервере и ещё раз в компоненте через useAsyncData, данные будут запрошены дважды. Проверяйте состояние store перед повторным fetch.
  • \n
  • Гидрация несоответствий: если сервер и клиент возвращают разные данные (таймзоны, random), React получит mismatch-ошибку. Store должен давать детерминированные данные при SSR.
  • \n
  • Circular imports: store может импортировать другой store через actions — следите за цикличными зависимостями, они приводят к undefined в runtime.
  • \n
"}}],"url":"https://talanto.work/interview-prep/pinia-integration"}
NuxtMiddleTechnical

Как интегрируется Pinia в Nuxt 3? Как сохранить состояние store между SSR и клиентом?

Pinia подключается через модуль @pinia/nuxt. Для гидрации SSR→клиент state передаётся через payload Nuxt автоматически; для persistence используют pinia-plugin-persistedstate с SSR-safe опциями.

Подключение Pinia в Nuxt 3

Установите модуль и добавьте его в конфиг:

npm install pinia @pinia/nuxt
// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@pinia/nuxt'],
});

После этого все store-файлы из stores/ становятся авто-импортируемыми. Функция defineStore тоже авто-импортируется — дополнительных импортов в компонентах не нужно.

Создание store

// stores/user.ts
export const useUserStore = defineStore('user', () => {
  const user = ref<{ id: number; name: string } | null>(null);
  const isLoggedIn = computed(() => user.value !== null);

  async function fetchUser(id: number) {
    const data = await $fetch(`/api/users/${id}`);
    user.value = data;
  }

  function logout() {
    user.value = null;
  }

  return { user, isLoggedIn, fetchUser, logout };
});

Использование store в компонентах и composables

// components/UserProfile.vue
<script setup lang="ts">
const userStore = useUserStore();
await userStore.fetchUser(1);
</script>

SSR-гидрация: как работает автоматически

Nuxt передаёт состояние Pinia через useNuxtApp().payload. При SSR-рендере store заполняется на сервере, сериализуется в HTML (в тег <script id="__NUXT_DATA__">), и клиент гидрирует его без повторных запросов. Это работает «из коробки» — вручную ничего сериализовать не нужно.

Инициализация store на сервере через plugins

// plugins/pinia-init.server.ts
export default defineNuxtPlugin(async () => {
  const userStore = useUserStore();
  // Заполняем store данными при каждом SSR-запросе
  const event = useRequestEvent();
  const token = getCookie(event, 'auth_token');
  if (token) {
    await userStore.fetchUser(/* decode token -> id */);
  }
});

Persist состояния между сессиями (localStorage)

npm install @pinia-plugin-persistedstate/nuxt
// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@pinia/nuxt', '@pinia-plugin-persistedstate/nuxt'],
});
// stores/cart.ts
export const useCartStore = defineStore('cart', () => {
  const items = ref<CartItem[]>([]);
  return { items };
}, {
  persist: {
    storage: piniaPluginPersistedstate.localStorage(),
    // На сервере localStorage недоступен — плагин автоматически пропускает
  },
});

Подводные камни

  • Singleton store на сервере: при SSR каждый запрос должен получать свой экземпляр Pinia. @pinia/nuxt создаёт его через useNuxtApp().$pinia — не обращайтесь к store вне контекста Nuxt-плагина или composable.
  • localStorage на сервере: localStorage недоступен при SSR. Если используете persist, убедитесь что плагин поддерживает SSR-режим, иначе получите ReferenceError.
  • useStore вне setup(): вызов store за пределами setup() или Nuxt-плагина вызовет ошибку «no active Pinia». Инициализируйте store только внутри composable-функций.
  • Дублирование fetch: если store инициализируется в плагине на сервере и ещё раз в компоненте через useAsyncData, данные будут запрошены дважды. Проверяйте состояние store перед повторным fetch.
  • Гидрация несоответствий: если сервер и клиент возвращают разные данные (таймзоны, random), React получит mismatch-ошибку. Store должен давать детерминированные данные при SSR.
  • Circular imports: store может импортировать другой store через actions — следите за цикличными зависимостями, они приводят к undefined в runtime.

Common mistakes

  • Путать Pinia in Nuxt с похожим API из соседнего фреймворка.
  • Не объяснять, где код выполняется: сервер, клиент, build step или runtime.
  • Игнорировать влияние на hydration, cache, bundle size или безопасность.

What the interviewer is testing

  • Точно объясняет назначение механизма «Pinia in Nuxt».
  • Показывает корректный минимальный пример без выдуманных API.
  • Называет ограничения, failure modes и production-компромиссы.

Sources

Related topics