Install the Compiler
Adding the compiler to your apps
For more background read the Compiler About page, for performance see Benchmarks.
It's important to note the compiler is optional, and it's much easier to get started by building your app out before you commit to the setup cost.
The compiler generates built versions of your components and config into a
.tamagui
directory. You'll want to add that directory to your .gitignore
.
Webpack
yarn tamagui-loader
We have a full example of a plain Webpack or Vite setup in the simple starter
accessible through npm create tamagui@latest
, which shows a complete
configuration with more detail.
Add tamagui-loader
and set up your webpack.config.js
.
You can set it up more manually like so:
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',// optional advanced optimization of styled() definitions within your app itself, not just ones in your `components` option// default is falseenableDynamicEvaluation: false,}const projectRoot = __dirnamemodule.exports = {resolve: {alias: {// Resolve react-native to react-native-web'react-native$': require.resolve('react-native-web'),// 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 fromexclude: path => shouldExclude(path, projectRoot, tamaguiOptions),use: [// optionally thread-loader for significantly faster compile!'thread-loader',// works nicely alongside esbuild{loader: 'esbuild-loader',},{loader: 'tamagui-loader',options: tamaguiOptions,},]}]},}
Or you can use the TamaguiPlugin which automates some of this setup for you:
const { TamaguiPlugin } = require('tamagui-loader')module.exports = {plugins: [new TamaguiPlugin({config: './tamagui.config.ts',components: ['tamagui'],importsWhitelist: ['constants.js', 'colors.js'],logTimings: true,disableExtraction: process.env.NODE_ENV === 'development',}),],
Some notes on the options:
- 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 your design system hot reloads more reliably.
Vite
See the Vite guide for more complete setup.
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
See the (guide) for more complete setup.
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-1192523231const 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:// disable static extraction, faster to iterate in dev mode (default false)disableExtraction: process.env.NODE_ENV === 'development',// Exclude react-native-web modules to lighten bundleexcludeReactNativeWebExports: ['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:// Watches a file that uses @tamagui/theme-builder// See the "Creating Themes" guidethemeBuilder: {input: './themes-input.tsx',output: './themes.tsx',},// adds mini-css-extract and css-minimizer-plugin, can fix issues with unique configurationsenableCSSOptimizations: false,// disable tamagui config to make fonts easier to importdisableFontSupport: 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
Note that the @tamagui/babel-plugin
is completely optional, and on native
Tamagui doesn't optimize as much as on web, so leaving it out is actually
recommended to start. If later on you feel the need for a bit more speed, you
can try adding it.
yarn @tamagui/babel-plugin
Add to your babel.config.js
:
module.exports = {plugins: [['@tamagui/babel-plugin',{components: ['tamagui'],config: './tamagui.config.ts',importsWhitelist: ['constants.js', 'colors.js'],logTimings: true,disableExtraction: process.env.NODE_ENV === 'development',}],]}
Currently the native compiler doesn't optimize as much as it could. It bails out
if it encounters any theme usage, like <View backgroundColor="$background" />
.
If you are on version 1.75
or greater, you can test enabling this experimental
optimization by adding a new property with the key
experimentalFlattenThemesOnNative
set to true in the above config object and
that will make the compiler to flat and extract any theme usage or dynamic
values.
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.
enableDynamicEvaluation
boolean
Default:
false
(Experimental) Enables further extracting of any styled component, even if not in your components. See below for more information.
experimentalFlattenThemesOnNative
boolean
Default:
false
(Experimental) Enables further extracting of components that use theme values on native.
emitSingleCSSFile
boolean
Default:
false
(Experimental) Combines all CSS into one file, only available for Webpack for now. Must add lightningcss version 1.1.x
Dynamic Evaluation
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 setting enableDynamicEvaluation
as seen above in the props table.
The way it works is, when the compiler detects a styled() expression outside one of the defined component directories, it will run the following:
- First, read the file and use a custom
babel
transform to force all top-level variables to be exported. - Then, run
esbuild
and bundle the entire file to a temporary file in the same directory, something like.tamagui-dynamic-eval-ComponentName.js
- Now, read the file in and load all new definitions found.
- 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.
Disabling the compiler
You can disable the compiler optimizations for an entire file with a comment at the top of your file:
// tamagui-ignore
You can disable the compiler optimization for a single component with the
boolean property disableOptimization
:
import { View } from '@tamagui/core'export default () => (<View disableOptimization />)
Previous
Background
Next
Benchmarks