How to Set Up Nuxt 4 with Pinia, UnoCSS, and i18n for TypeWords
You can set up Nuxt 4 with Pinia, UnoCSS, and i18n by installing @pinia/nuxt, @unocss/nuxt, and @nuxtjs/i18n, then registering them in nuxt.config.ts and configuring stores, CSS shortcuts, and locale files according to the TypeWords repository structure.
The TypeWords project by zyronon demonstrates a production-ready Nuxt 4 architecture that combines type-safe state management, utility-first styling, and multilingual routing. This guide walks you through the exact implementation used in the source code to configure these three core modules, referencing specific file paths and methods from zyronon/TypeWords.
Install the Required Dependencies
First, add the three Nuxt modules to your project. The TypeWords repository uses pnpm as its package manager.
pnpm i @pinia/nuxt @unocss/nuxt @nuxtjs/i18n
@pinia/nuxtintegrates Pinia with Nuxt’s auto-import system, replacing the legacy Vuex store.@unocss/nuxtbrings the UnoCSS engine, preset icons, and directive transformers.@nuxtjs/i18nadds locale-aware routing and translation loading capabilities.
Configure the Nuxt 4 Modules
Register Modules in nuxt.config.ts
In the TypeWords project, nuxt.config.ts registers all three modules in the modules array. This enables auto-importing for Pinia stores, UnoCSS utilities, and i18n composables across your application.
// nuxt.config.ts
export default defineNuxtConfig({
modules: [
'@pinia/nuxt', // State management
'@unocss/nuxt', // Utility-first CSS
'@nuxtjs/i18n', // Internationalization
// ...other modules
],
})
Configure i18n Settings
The i18n configuration in nuxt.config.ts defines available locales, default language, and routing strategy. TypeWords uses a no_prefix strategy to serve different languages without changing the URL structure.
// nuxt.config.ts
export default defineNuxtConfig({
i18n: {
locales: [
{ code: 'en', file: 'en.json', name: 'English' },
{ code: 'zh', file: 'zh.json', name: '中文' },
],
defaultLocale: 'zh',
strategy: 'no_prefix',
},
})
Translation files are stored in the i18n/locales/ directory (e.g., en.json, zh.json) and automatically loaded by the module.
Set Up Pinia Stores
Pinia stores in TypeWords live under app/core/stores/ and use the defineStore function from pinia. The setting.ts store demonstrates reactive state management with persistence capabilities.
// app/core/stores/setting.ts
import { defineStore } from 'pinia'
export const useSettingStore = defineStore('setting', {
state: () => ({
soundType: 'us',
theme: 'auto',
// additional state properties
}),
actions: {
setState(obj) {
this.$patch(obj)
},
async init() {
// Loads persisted settings from IndexedDB
},
},
})
The $patch method efficiently updates multiple state properties, while the init action handles client-side hydration of stored user preferences.
Configure UnoCSS with Custom Shortcuts
UnoCSS configuration resides in uno.config.ts at the project root. The TypeWords setup defines shortcuts—custom utility classes that expand to multiple CSS rules—and configures presets for icons and attributify mode.
// uno.config.ts
import { defineConfig } from 'unocss'
export default defineConfig({
shortcuts: {
'bg-primary': 'bg-[var(--color-primary)]',
'card': 'rounded-xl p-4 mb-8 shadow-lg box-border relative bg-second',
},
// presets, theme, and transformers follow
})
These shortcuts allow you to write <div class="card"> in templates, which UnoCSS expands to the full set of utility classes at build time.
Use the Stack in Vue Components
With all three modules configured, you can access Pinia state, i18n translations, and UnoCSS utilities simultaneously in your Vue Single File Components.
<script setup lang="ts">
import { useSettingStore } from '~/app/core/stores/setting'
import { useI18n } from 'vue-i18n'
const setting = useSettingStore()
const { locale, t } = useI18n()
function changeLang(lang: string) {
locale.value = lang
}
</script>
<template>
<div class="card bg-primary">
<h1 class="text-2xl font-bold">{{ $t('welcome') }}</h1>
<p>Current theme: {{ setting.theme }}</p>
<button @click="changeLang('en')">{{ t('button.en') }}</button>
<button @click="changeLang('zh')">{{ t('button.zh') }}</button>
</div>
</template>
The $t function provides global access to translations, while useSettingStore() delivers reactive, type-safe state. UnoCSS processes the card and bg-primary classes using the shortcuts defined in uno.config.ts.
Summary
- Install
@pinia/nuxt,@unocss/nuxt, and@nuxtjs/i18nvia your package manager to enable the three core features. - Register all three modules in
nuxt.config.tsmodules array to activate auto-imports and plugin injection. - Define Pinia stores in
app/core/stores/usingdefineStorefor centralized, type-safe state management. - Configure UnoCSS shortcuts in
uno.config.tsto create reusable utility classes likecardandbg-primary. - Store translation JSON files in
i18n/locales/and reference them in thenuxt.config.tsi18n configuration. - Access global utilities like
$tthrough the client-side plugin atapp/plugins/02.init.client.tsor viauseI18n()composable.
Frequently Asked Questions
What is the difference between Pinia and Vuex in the TypeWords setup?
Pinia replaces Vuex as the official state management solution for Vue 3 and Nuxt 4, offering better TypeScript inference and a simpler API with defineStore. In app/core/stores/setting.ts, Pinia's $patch method allows partial state updates that Vuex mutations handled more verbosely.
How does UnoCSS differ from Tailwind CSS in this configuration?
While Tailwind requires a full CSS purge and JIT compiler, UnoCSS generates utilities on-demand based on detected class names, resulting in zero-config builds and smaller bundles. The uno.config.ts file in TypeWords uses shortcuts to create semantic tokens like card, combining multiple utilities into single, maintainable classes without writing custom CSS.
Why does TypeWords use the no_prefix i18n strategy?
The no_prefix strategy serves different languages from the same URL path (e.g., /dashboard remains /dashboard for both English and Chinese users), relying on cookies or browser detection instead of URL segments. This preserves SEO value for single-language deployments while still allowing runtime language switching via the locale reactive property from useI18n().
How do I access Pinia stores inside Nuxt plugins?
You can access Pinia stores in client-side plugins like app/plugins/02.init.client.ts by importing the store composable after the Pinia instance initializes. Since @pinia/nuxt auto-registers the Pinia plugin, calling useSettingStore() inside a plugin that runs after application initialization provides full access to state and actions for setting up global utilities or hydration logic.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →