Install the Compiler

Adding the compiler to your apps.

Tamagui works 100% at runtime, the compiler is optional, it's easier to get started by skipping setting it up so you can try it out in practice before committing.

Install on most platforms is as easy as configuring a library:

The compiler will generate built versions of your components and config into the .tamagui directory, relative to your app. You'll want to add this to your .gitignore if you don't already ignore dotfiles.

Setup

Be sure to set TAMAGUI_TARGET to "native" when targeting native platforms, and set it to "web" for web apps.

Webpack

yarn add tamagui-loader

We have a full example of a plain, webpack-only repo here , which should capture the complete configuration more accurately.

Add tamagui-loader and set up your webpack.config.js:

const { shouldExclude } = require('tamagui-loader')
const tamaguiOptions = {
config: './tamagui.config.ts',
components: ['tamagui'],
importsWhitelist: ['constants.js', 'colors.js'],
logTimings: true,
disableExtraction: process.env.NODE_ENV === 'development',
}
const projectRoot = __dirname
module.exports = {
resolve: {
alias: {
// Resolve react-native to react-native-web
'react-native$': require.resolve('react-native-web'),
// Experimentally opt into react-native-web-lite which drops support for all react-native
// built-in List components, and removes many deprecated APIs and code-reduction, slimming
// bundle sizes down nearly 30-50Kb.
'react-native$': 'react-native-web-lite',
'react-native-web$': 'react-native-web-lite',
// optional, for lighter svg icons on web
'react-native-svg': require.resolve('@tamagui/react-native-svg'),
}
},
module: {
rules: [
{
test: /\.[jt]sx?$/,
// you'll likely want to adjust this helper function,
// but it serves as a decent start that you can copy/paste from
exclude: path => shouldExclude(path, projectRoot, tamaguiOptions),
use: [
// optionally thread-loader for significantly faster compile!
'thread-loader',
// works nicely alongside esbuild
{
loader: 'esbuild-loader',
options: {
loader: 'tsx',
},
},
{
loader: 'tamagui-loader',
options: tamaguiOptions,
},
]
}
]
},
plugins: [
new webpack.DefinePlugin({
'process.env.TAMAGUI_TARGET': '"web"',
})
]
}
  • importsWhitelist: Tamagui takes a conservative approach to partial evaluation, this field whitelists (matching against both .ts and .js) files to allow files that import them to read and use their values during compilation. Typically colors and constants files.
  • disableExtraction: Useful for faster developer iteration as themes hot reload more reliably.

Vite

Add @tamagui/vite-plugin and update your vite.config.ts:

import { tamaguiExtractPlugin, tamaguiPlugin } from '@tamagui/vite-plugin'
export default defineConfig({
plugins: [
tamaguiPlugin({
config: './src/tamagui.config.ts',
components: ['tamagui'],
}),
// optional, adds the optimizing compiler:
tamaguiExtractPlugin(tamaguiConfig),
],
})

Next.js

Add @tamagui/next-plugin and configure your next.config.js. Here we show a fuller scope of the options

// note next-compose-plugins somewhat unmaintained
// you can use a simple two-liner instead, see:
// https://github.com/cyrilwanner/next-compose-plugins/issues/59#issuecomment-1192523231
const withPlugins = require('next-compose-plugins')
const { withTamagui } = require('@tamagui/next-plugin')
export default withPlugins([
withTamagui({
config: './tamagui.config.ts',
components: ['tamagui'],
// rest are all optional:
// Experimentally opt into react-native-web-lite which drops support for all react-native
// built-in List components and removes many deprecated APIs for code-reduction, slimming
// bundle sizes down nearly 30-50Kb.
useReactNativeWebLite: true,
// disable static extraction, faster to iterate in dev mode (default false)
disableExtraction: process.env.NODE_ENV === 'development',
// Exclude react-native-web modules to lighten bundle
excludeReactNativeWebExports: ['Switch', 'ProgressBar', 'Picker'],
// By default, we configure webpack to pass anything inside your root or design system
// to the Tamagui loader. If you are importing files from an external package, use this:
shouldExtract: (path: string, projectRoot: string) => {
if (path.includes('../packages/myapp')) {
return true
}
},
// Advanced:
// adds mini-css-extract and css-minimizer-plugin, can fix issues with unique configurations
enableCSSOptimizations: false,
// disable tamagui config to make fonts easier to import
disableFontSupport: false,
// Many packages give difficulty to the nextjs server-side (node) runtime when un-bundled.
// for example, tamagui configures aliases like `react-native` => `react-native-web`.
// if you're running into a module that has errors importing react-native, you'll want to
// use a custom shouldExcludeFromServer function to include it (or override the default).
// this is the exact same return type as webpack.externals.
// returning undefined will let tamagui handle it, boolean or other values to override.
shouldExcludeFromServer: ({ fullPath, request }) => {
if (fullPath.includes('my-module')) {
return `commonjs ${commonjs}`
}
if (request === 'some-hard-to-bundle-package') {
return true
}
},
})
])

Note: If running into issues, the environment variable IGNORE_TS_CONFIG_PATHS to "true" can fix issues with Tamagui being resolved incorrectly.

See the Next.js Guide for more details on setting up your app.

Babel / Metro

yarn add @tamagui/babel-plugin babel-plugin-transform-inline-environment-variables

Add @tamagui/babel-plugin to your babel.config.js plugins:

// only use babel-plugin for native:
process.env.TAMAGUI_TARGET = 'native'
module.exports = {
plugins: [
[
'@tamagui/babel-plugin',
{
components: ['tamagui'],
config: './tests/lib/tamagui.config.js',
importsWhitelist: ['constants.js', 'colors.js'],
logTimings: true,
disableExtraction: process.env.NODE_ENV === 'development',
}
],
// be sure to set TAMAGUI_TARGET
['transform-inline-environment-variables', {
include: 'TAMAGUI_TARGET'
}]
]
}

We've noticed some issues with @types/react-native version 0.65.5, you'll want to upgrade to at least 0.66.6.

Expo

Check out the Expo guide for more information on setting up Expo.

Web-only apps

If you want autocompleted imports of react-native without having to install all the weight of react-native, you can set react-native version to 0.0.0, and add @types/react-native at the latest version.

Props

All compiler plugins accept the same options:

Props

  • config (required)

    string

    Relative path to your tamagui.config.ts file which should export default the result from createTamagui.

  • components

    string[]

    Default: 

    ['tamagui']

    Array of npm modules containing Tamagui components which you'll be using in your app. For example: if you are using the base Tamagui components. This directs the compiler to load and optimize.

  • importsWhitelist

    string[]

    Array of whitelisted file paths (always end in .js) which the compiler may try and import and parse at build-time. It is normalized to ".js" ending for all file extensions (js, jsx, tsx, ts). This usually should be set to something like ['constants.js', 'colors.js'] for example, where you have a couple mostly static files of constants that are used as default values for styles.

  • logTimings

    boolean

    Default: 

    true

    Tamagui outputs information for each file it compiles on how long it took to run, how many components it optimized, and how many it flattened. Set to false to disable these logs.

  • disable

    boolean

    Default: 

    false

    Disable everything - debug and extraction.

  • disableExtraction

    boolean

    Default: 

    false

    Disable extraction to CSS completely, instead fully relying on runtime. Setting this to true speed up development as generally your app will hot reload the Tamagui configuration itself.

  • disableDebugAttr

    boolean

    Default: 

    false

    If enabled along with disableExtraction, all parsing will turn off. Normally turning off disableExtraction will keep the helpful debug attributes in DOM

  • disableFlattening

    boolean

    Default: 

    false

    Turns off tree-flattening.

  • Dynamic Loading

    By default the Tamagui compiler only optimizes styled expressions found in the modules defined by your components config. This means if you do an inline styled() inside your actual app directory, it will default to runtime style insertion.

    This is typically Good Enough™️. As long as you define most of your common components there, you'll get a very high hit rate of compiled styles being used and runtime generation being skipped, as atomic styles with your design system tokens will be mostly pre-generated.

    Tamagui has experimental support for loading any component, even if it occurs somewhere outside your configured components modules. This is called "dynamic loading", for now. You can enable it with the following environment variable:

    TAMAGUI_ENABLE_DYNAMIC_LOAD=1

    The way it works is, when the compiler detects a styled() expression outside one of the defined component directories, it will run the following:

    1. First, read the file and use a custom babel transform to force all top-level variables to be exported.
    2. Then, run esbuild and bundle the entire file to a temporary file in the same directory, something like .tamagui-dynamic-eval-ComponentName.js
    3. Now, read the file in and load all new definitions found.
    4. Finally, continue with optimization, using the newly optimized component.

    You may see why this is experimental. It's very convenient as a developer, but has a variety of edge cases that can be confusing or breaking, and we want to avoid installation woes. Though it does continue on error and work generally, it outputs warnings in Webpack currently due to our plugin not properly indicating to Webpack about the new files (a fixable bug), which causes big yellow warning output and a cache de-opt.

    We're leaving this feature under the environment variable while it matures. Let us know if you find it useful.