Installation
How to get Tamagui set up.
Tamagui is a full-featured UI kit built on top of @tamagui/core
.
If you want a complete set of components designed to build universal apps, use tamagui
. If you want to build out your own components and don't want any of the tamagui
components, you can just use @tamagui/core
. See the Design Systems guide for more on how to configure Core.
This document uses tamagui
as an example, but if you are using @tamagui/core
you can swap out the import.
Saving bundle size for web-only use cases
If you are only targeting the web: @tamagui/web
is the same library as @tamagui/core
, except that it drops support for a few React Native specific things, mostly props: namely the GestureResponderHandlers
and onLayout
props are absent, and the platform methods on refs like measureInWindow
are absent. If you are only targeting web, you can swap every reference of tamagui
or @tamagui/core
in this guide with @tamagui/web
. Note that you can't use @tamagui/web
with tamagui
as the full tamagui
package relies on many of these props.
Quick Start
Try out create-tamagui for a helpful starter template which comes with @tamagui/shorthands
and @tamagui/themes
for easy default configuration:
npm create tamagui@latest
This gives you everything pre-configured for full-stack apps with shared code.
Install
yarn add tamagui
You generally need to configure one or two things: passing the environment variable TAMAGUI_TARGET
(set to the string web
or native
, respectively), and on the web, setting an alias from react-native
to react-native-web
.
Web
To pass the ENV you add it to the beginning of your start script generally like so:
{"scripts": {"dev": "TAMAGUI_TARGET=web webpack"}}
Tamagui has plugins that make the rest a bit simpler to set up (and also get you the optimizing compiler) which you can find in the Compiler Install docs.
You just want to ensure two things happen: the TAMAGUI_TARGET
environment variable is passed in, and react-native
is aliased to react-native-web
(only if using tamagui
or other React Native APIs yourself, as core it doesn't have a dependency on react-native at all).
Webpack
We also recommend passing in __DEV__
and NODE_ENV
if using react-native:
config.plugins.push(new webpack.DefinePlugin({process: {env: {TAMAGUI_TARGET: JSON.stringify('web'),DEV: process.env.NODE_ENV === 'development' ? 'true' : 'false',NODE_ENV: JSON.stringify(process.env.NODE_ENV),},},}))config.resolve.alias['react-native$'] = 'react-native-web'// or react-native-web-lite if using our more modern but less supported version// set up web extensionscompiler.options.resolve.extensions = ['.web.tsx','.web.ts','.web.js','.ts','.tsx','.js',]
Vite
config.define = {DEV: `${process.env.NODE_ENV === 'development' ? true : false}`,'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),'process.env.TAMAGUI_TARGET': JSON.stringify('web'),}config.resolve.alias['react-native'] = 'react-native-web'// or react-native-web-lite if using our more modern but less supported version// set up web extensionsconfig.optimizeDeps.esbuildOptions = {...config.optimizeDeps.esbuildOptions,resolveExtensions: ['.web.js','.web.ts','.web.tsx','.js','.jsx','.json','.ts','.tsx','.mjs',],loader: {'.js': 'jsx',},}
Native
Babel
For native you'd do something like this:
TAMAGUI_TARGET=native react-native start
TAMAGUI_TARGET=native expo start
You'll also need to indicate to Metro and babel to inline TAMAGUI_TARGET like so:
yarn add @tamagui/babel-plugin babel-plugin-transform-inline-environment-variables
Add @tamagui/babel-plugin
to your babel.config.js
plugins:
// just to ensure TAMAGUI_TARGET is setprocess.env.TAMAGUI_TARGET = 'native'module.exports = {plugins: [['transform-inline-environment-variables', {include: ['TAMAGUI_TARGET']}]]}
Media Queries
This is also documented in Configuration, but worth noting twice.
@tamagui/core
doesn't provide media capabilities out of the box to native apps, while on the web it uses native media queries. To enable media queries for React Native, you need to provide matchMedia implementation:
yarn add @tamagui/react-native-media-driver
import { createMedia } from '@tamagui/react-native-media-driver'export default createTamagui({media: createMedia({xs: { maxWidth: 660 },// ...}),})
Config file
Create a tamagui.config.ts
file at the root of your project.
You can import createTamagui
from either tamagui
or @tamagui/core
depending on which you decide to use.
From there you'll export default the result of createTamagui
like so:
import { shorthands } from '@tamagui/shorthands'import { themes, tokens } from '@tamagui/themes'import { createFont, createTamagui } from 'tamagui'export default createTamagui({themes,tokens,shorthands,fonts: {body: createFont({family: 'Arial',size: {// You'll want to fill these values in so you can use them// for now, fontSize="$4" will be 14px.// You can define different keys, but `tamagui`// standardizes on 1-15.4: 14,},lineHeight: {4: 16,},}),},})
You'll need to install these packages if you want the Tamagui pre-configured design system:
yarn add @tamagui/shorthands @tamagui/themes
For an even quicker start, you can get a full design system with @tamagui/config
, which we show on the Configuration page. That page also goes into more detail on everything createTamagui
accepts.
Root export
Import and use the TamaguiProvider
component as the top component in your app.
// this provides some helpful reset styles to ensure a more consistent look// only import this from your web app, not nativeimport '@tamagui/core/reset.css'import React, { Suspense } from 'react'import { TamaguiProvider } from 'tamagui'import config from './tamagui.config'export default function App() {return (<TamaguiProvider config={config}>{/* if you want nice React 18 concurrent hydration, you'll want Suspense near the root */}<Suspense><AppContents /></Suspense></TamaguiProvider>)}
TamaguiProvider has optional props, see the docs for them here.
Webpack, Vite and other web bundlers
Each bundler has a plugin that helps automate setup. See these docs for more:
For a minimal install without any compiler setup, you'll just need to alias react-native
to react-native-web
(or react-native-web-lite
if you're feeling adventurous and want better concurrent mode support and a more up to date RN Animated driver).
For Webpack:
config.resolve.alias['react-native$'] = require.resolve('react-native-web')
Or alternatively, to react-native-web-lite
, our more experimental but modern version of react-native-web.
Other bundlers and frameworks
Remember, Tamagui runs fully at runtime, so you don't need to set up the compiler to start. But because Tamagui imports react-native
directly, for web platforms you need to configure an alias. And for CSS extraction, on some frameworks like Next.js require extra configuration.
We have a few guides for the compiler that also include setup of any extras for the web:
Just note: you don't need to set up the compiler!
For the web you usually just need to alias react-native
to react-native-web
if you are using @tamagui/core
or tamagui
. If you are using the lowest level @tamagui/web
package, which is basically core but without the React Native unified event APIs, you don't need anything else as it doesn't rely on react-native
at all.
Done!
You're ready to use Tamagui:
import { Button } from 'tamagui'export default function Demo() {return <Button>Button</Button>}
We recommend spending time understanding configuration from here, skipping over the Compiler setup. The compiler is optional and should be saved for setting up after you've built something you'd like to test it with.