Vite Guide

How to set up Tamagui with Vite

Tamagui’s Vite integration loads your config and can extract component styles to CSS at build time.

The Vite plugin and the tamaguiAliases helper are exported from @tamagui/cli/vite.

Install

The Vite plugin is ESM-only. Your project must have "type": "module" in its package.json (or use .mjs/.mts config files). CJS (require()) is not supported.

For a full-featured example, you can create a new app using npm create tamagui@latest and select the ‘Remix’ option, which includes a Vite setup.

Create a new Vite project:

yarn create vite@latest

Add @tamagui/cli, which carries the Vite plugin along with the CLI and the other bundler integrations:

yarn add -D @tamagui/cli

If Vite is the only bundler this project will ever use, yarn add @tamagui/vite-plugin on its own is a smaller install and exports exactly the same things. Swap the import in the examples below if you go that way.

Configuration

Update your vite.config.ts. If you have a tamagui.build.ts (recommended; see compiler install docs), no options are needed:

vite.config.ts

import { tamaguiPlugin } from '@tamagui/cli/vite'
export default {
plugins: [
// reads from tamagui.build.ts automatically
tamaguiPlugin(),
].filter(Boolean),
}

Or pass options inline:

vite.config.ts

import { tamaguiPlugin } from '@tamagui/cli/vite'
export default {
plugins: [
tamaguiPlugin({
config: 'src/tamagui.config.ts',
components: ['tamagui'],
disableExtraction: true,
}),
].filter(Boolean),
}

During development, the plugin evaluates your configuration through Vite’s module runner and watches its imported dependencies. Saving a token or theme updates outputCSS, .tamagui/tamagui.config.json, and .tamagui/prompt.md before the browser reloads. Unchanged artifacts are not rewritten. You do not need to run tamagui generate after each edit.

Use disableExtraction: true to keep runtime styling while retaining configuration generation. disable: true disables configuration loading as well.

Or use a minimal manual setup for Vite that just adds compatibility for react-native-web and React Native extensions:

config.define = {
DEV: `${process.env.NODE_ENV === 'development' ? true : false}`,
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
}
config.resolve.alias['react-native'] = 'react-native-web'
// set up web extensions
config.optimizeDeps.esbuildOptions = {
...config.optimizeDeps.esbuildOptions,
resolveExtensions: [
'.web.js',
'.web.jsx',
'.web.ts',
'.web.tsx',
'.mjs',
'.js',
'.mts',
'.ts',
'.jsx',
'.tsx',
'.json',
],
loader: {
'.js': 'jsx',
},
}

Custom Aliases with tamaguiAliases

For advanced use cases where you need more control over alias ordering in your Vite config, you can use the tamaguiAliases helper function:

vite.config.ts

import { tamaguiAliases } from '@tamagui/cli/vite'
export default {
resolve: {
alias: [
// your custom aliases first
{ find: '@app', replacement: '/src' },
// then tamagui aliases
...tamaguiAliases({
// use @tamagui/react-native-web-lite for smaller bundle
rnwLite: true,
// or 'without-animated' for even smaller bundle (no Animated API)
// rnwLite: 'without-animated',
// alias react-native-svg to @tamagui/react-native-svg
svg: true,
}),
],
},
}

This is useful when you need to ensure specific alias resolution order or when using a custom Vite setup without the full tamaguiPlugin.