# Tamagui Complete Documentation > Tamagui is a complete UI solution for React Native and Web, with a fully-featured UI kit, styling engine, and optimizing compiler. ## intro/benchmarks --- title: Benchmarks description: Reproducible web and native performance comparisons --- Uniwind was fastest in all 15 native cells. Tamagui Tailwind was second in all 15. On web, Tailwind CSS was fastest in 10 cells, Tamagui Tailwind in three, and inline React in two. Green cells mark the lowest mean in each row. All values are mean milliseconds. Lower is faster. ## Web results The web fixture renders 200 items, or 60 in the heavy case. Mount starts with an empty root. Update changes fixture state without replacing the root. Remount replaces the tree. Timing starts before the React state change and ends in `useLayoutEffect`. Each production build ran in Chromium `151.0.7922.34` on an Apple M5 Pro with 18 logical CPUs and 64 GiB of memory. The runner used Bun `1.3.14` on Darwin `25.5.0`. Two warmup rounds preceded 10 retained rounds. Seed `73129` shuffled every framework and scenario round. All 50 behavior checks passed before timing. Simple, rich, group, and heavy use Tamagui `3.0.0-beta.901.1`. Animated uses Tamagui `3.0.0-beta.917.1`. The animated compiler flattened all 14 candidates with zero bailouts. The other versions are Tailwind CSS `4.3.0`, NativeWind `5.0.0-preview.4`, React Native Web `0.21.2`, Uniwind `1.11.0`, React `19.2.3`, and React DOM `19.2.3`. Tamagui Tailwind mounted faster than NativeWind and Uniwind in all five web scenarios. The paired 95% confidence intervals favored Tamagui in each of those 10 comparisons. Tailwind CSS had lower means than Tamagui in 10 of 15 cells. Inline React had the lowest simple and group update means. The Tamagui arm uses `@tamagui/tailwind`. This table does not measure Tamagui's component APIs. ## Native results All three Release apps render the same `native-class-bench.tsx` fixture. They use the same class strings, component tree, item counts, state changes, and timing boundary. Simple, themed, rich, and group render 200 items. Heavy renders 60 three-component groups. The fixture uses explicit point dimensions. An earlier smoke run found that `w-5` resolved to 52 points under the benchmark's Tamagui config and 20 points in the other arms. That fixture was rejected. Fixture version 2 passed an independent 20 by 20 layout check in all three apps before timing. The campaign used Tamagui Tailwind `3.0.0-beta.917.1`, NativeWind `5.0.0-preview.4`, and Uniwind `1.11.0`. NativeWind also used `react-native-css` `3.0.7`, Reanimated `4.5.1`, and Worklets `0.10.1`. Every app used Expo `57.0.15`, React `19.2.3`, and React Native `0.86.2`. The apps ran on an iOS `26.4` simulator on an Apple M5 Pro with 18 logical CPUs and 64 GiB of memory. The runner used Bun `1.4.0`, Node `26.3.0`, and XcodeBuildMCP `2.3.0` on Darwin `25.5.0`. It waited until three host samples were `71.8%`, `63.4%`, and `74.1%` idle. Two warmup rounds preceded 12 retained rounds. Seed `73129` shuffled every round. Uniwind had the lowest mean in every native cell. Its paired 95% confidence intervals also favored it over Tamagui in all 15 cells. Tamagui had lower means than NativeWind in all 15 cells. Several Tamagui and NativeWind paired intervals overlapped zero because NativeWind's observations varied widely. ## Source record The cross-framework harness began in `code/comparisons`. It was extracted from this monorepo in [commit `300a4db8de`](https://github.com/tamagui/tamagui/commit/300a4db8def094229793e2c416b1a008fc5d20a5) so NativeWind does not enter the main workspace dependency graph. The web values come from committed receipts `8b45f79809` and `9896f9c488`. The native values come from fixture source commit `84fc84bb5d` and receipt commit `352925a2ac`. The native receipt contains all 180 retained observations, 30 warmups, confidence intervals, package versions, fixture hashes, bundle hashes, build identities, and behavior signatures. The comparison repository does not have a public remote yet. The web table is also retained in the [Tamagui Tailwind post](/blog/tailwind-mode). The monorepo still contains Tamagui's conformance harness, runtime microbenchmarks, compiler graph benchmark, bundle checks, and browser fixture. ## What this does not measure - The web run covers production rendering after load. It does not measure server rendering, hydration, interaction latency, or network cost. - The native run uses one simulator. It does not measure a physical device, Android, startup, memory, GPU work, or energy use. - The timers end in `useLayoutEffect`. They include React reconciliation and the synchronous native commit. They do not include the next fully drawn frame. - The behavior gate checks this fixture. It does not prove support or visual equivalence for every utility. - Compiler acceptance and rendered pixels are separate questions. - Small differences can move with hardware, operating system, library version, and application shape. - Bundle size and build time are tracked separately. Each library ships a different mix of runtime and build tooling. ## intro/styles --- title: Styling description: Style props, conditional values, and the modifiers that control them --- Tamagui uses style props across all components. Every element accepts React Native and web styles, with unified support for responsive, theme, and pseudo-state conditions. ```tsx import { Text, View } from 'tamagui' export const Card = () => ( Hello ) ``` `padding`, `borderRadius`, and `backgroundColor` are style props. `"4"` and `"lg"` are tokens, `background` is a theme key, and `md:` and `hover:` are modifiers. That is the whole grammar. The same props go on `View`, `Text`, every other [component](/docs/intro/components), and the second argument of [`styled()`](/docs/core/styled). Every style prop takes its value as a string or an object. Strings fit the base value and its conditions into one short read: ```tsx ``` Objects spell the same thing with keys, and stay fully typed: ```tsx ``` Use strings for short, mostly static values — a prop with one or two conditions, written inline. They exist so the common case stays terse and readable. Use objects when the value is a number (numbers skip token lookup, so `padding={16}` is always 16 pixels), when conditions stack up, or when you build the value in code from variables and spreads — objects are real values, so TypeScript checks every token and modifier as you type. Both spellings take the same values and modifiers and render identically, so mix them per prop as needed. The Conditional values section below details each spelling. ## Values A style value is one of: | Value | Example | Resolves to | | --- | --- | --- | | Number | `padding={16}` | 16 pixels on every platform | | Pixel string | `padding="16px"` | An exact pixel value, even when a token has the same name | | Token | `padding="4"`, `borderRadius="lg"` | The token from that prop's category | | Theme key | `color="color-11"` | The active theme's value, updating when the theme changes | | CSS value | `display="flex"`, `boxShadow="0 2px 8px shadow-3"` | Passed through on web and converted on native | | `rem` | `padding="1rem"` | Scaled by the user's font size on native | [Design system](/docs/intro/design-system) covers where tokens and theme keys come from. [Style props](/docs/core/style-props) lists every prop, including the ones that only render on web. ## Conditional values Every style prop takes conditions in two spellings, a string of clauses or a flat object. These render the same thing: String Typed ```tsx ``` ```tsx ``` The object keys are the same modifiers the string uses, with `default` for the base value. There is no mode to pick, and you can mix them per prop in one component: ```tsx ``` Strings are shorter for one or two conditions. Objects work better when the values are numbers, when the conditions stack up, or when you build the value in code. Both compile to identical CSS. An object only counts as conditional when it has a `default` key or its first key is a valid modifier. A plain value object like `shadowOffset={{ width: 0, height: 2 }}` is left alone. ## Font size and line height values Numeric `lineHeight` authored in Tamagui styles is a font-size ratio on web and native. A numeric string has the same ratio meaning when it is not a configured font-token name. An explicit px string stays absolute. Values are never inferred from magnitude, so `lineHeight={24}` means 24 times the effective font size and `lineHeight="24px"` means 24 pixels. Font configuration keeps a separate compatibility contract. Numeric `lineHeight` entries remain absolute pixels. Use a numeric string for a relative font token and a px string for an explicit absolute token: ```tsx Platform line height Exact px size 30px line box 24px line box const bodyFont = createFont({ family: 'Inter', size: { body: 20 }, lineHeight: { legacy: 24, // absolute pixels relative: '1.5', // ratio explicit: '24px', // absolute pixels }, }) ``` On native, Tamagui can resolve a ratio from the effective font size inherited through Tamagui text. This inheritance continues through intervening Tamagui Views, as it does on web. When a Tamagui text boundary sits below a raw React Native or third-party text parent, pass the effective font size at that boundary because Tamagui cannot inspect typography held only by the native host tree. If a native ratio has no known font size, Tamagui explicitly sets `fontSize={14}` and calculates the line height from 14. This also applies below a raw native text parent, so pass its font size at the Tamagui boundary to preserve the glyph size. ## Modifiers Every modifier applies to just the property carrying it. They fall into five groups: | Group | Modifiers | Example | | --- | --- | --- | | State | `hover:`, `press:`, `focus:`, `focus-visible:`, `focus-within:`, `disabled:` | `scale="press:0.98"` | | Screen size | any media name from your config, such as `sm:` or `max-md:` | `padding="4 md:8"` | | Theme | `dark:`, `light:`, or a top-level theme name | `color="dark:white"` | | Platform | `web:`, `native:`, `ios:`, `android:` | `paddingTop="ios:12"` | | Parent | `group-hover:`, `group-press:`, `group-focus:`, `@sm:` container queries | `color="group-hover:white"` | Modifiers stack. `sm:hover:purple` applies on hover at the `sm` breakpoint. When several clauses match, the most specific one wins. [Flat conditional values](/docs/guides/flat-values) has the full precedence rules and grammar. ### States Add state clauses directly to the property they change: - **`hover:`**: while hovering (web only, maps to CSS `:hover`) - **`press:`**: while pressing - **`focus:`**: while focused (maps to CSS `:focus`) - **`focus-visible:`**: while keyboard-focused (maps to CSS `:focus-visible`) - **`focus-within:`**: while a child has focus (maps to CSS `:focus-within`) - **`disabled:`**: while `disabled={true}` ```tsx ``` `hover:` is web only. `focus:`, `focus-visible:`, and `focus-within:` map to the matching CSS pseudo-classes on web. ### Enter and exit For mount and unmount animations, `enter:` is the value the property animates from on mount, and `exit:` is the value it animates to on unmount: ```tsx ``` See [Animations](/docs/core/animations). ### Screen size Media queries come from `createTamagui({ media })`. Each name is a modifier on native and web: ```tsx ``` ### Theme and platform Theme modifiers target top-level themes. With `light` and `light_subtle` defined, only `light` can be targeted. Platform modifiers cover `ios`, `android`, `web`, and `native`: ```tsx ``` ### Groups Mark a parent with `group` and children can react to its state: ```tsx ``` The states are `hover` (web only), `press`, and `focus`. Name a group to target it from further down: ```tsx Inner Sibling ``` `Inner` turns blue when the `card` group is hovered. `Sibling` turns green when its nearest group is hovered. For completion on named groups, declare them next to your config types: ```tsx declare module 'tamagui' { interface TamaguiCustomConfig extends AppConfig {} interface TypeOverride { groupNames(): 'card' | 'header' | 'sidebar' } } ``` ### Container queries Add `container` to a group and children can respond to the parent's size, using the same media names. Web outputs a CSS container query: ```tsx ``` Named containers work the same way with `@sm/card:`. On native the container's size is only known after the first `onLayout`, so children render once before the query applies. Set `untilMeasured="hide"` on the container to keep it at opacity 0 until measured, or give the container an explicit `width` and `height` so children can resolve the query on first render. ## Next - [Style props](/docs/core/style-props) for every accepted property and how it renders per platform. - [styled()](/docs/core/styled) and [Variants](/docs/core/variants) for reusable components. - [Flat conditional values](/docs/guides/flat-values) for the complete value grammar and editor support. ## intro/introduction --- title: Introduction --- ## Two ways to write styles Tamagui ships two styling frontends. Both compile to the same atomic CSS on web and the same native styles on iOS and Android, and both accept the same tokens, themes, and media queries. **Style props** on `View` and `Text` are the default. They are typed and compile to atomic CSS on web or native styles on iOS and Android: ```tsx import { Text, View } from 'tamagui' export const Card = () => ( Hello World ) ``` **Tailwind** classes on `className` are the other option, for teams who already think in utilities: ```tsx import { Text, View } from '@tamagui/tailwind' export const Card = () => ( Hello World ) ``` The choice is per import path, so one app can use both. Read [Components](/docs/intro/components) for the style prop path and [Tailwind](/docs/core/tailwind) for the utility path. ## Why a compiler Cross-platform development often involves trade-offs between native feel, code reuse, and performance. React Native shares logic across platforms, but on web, styling in JavaScript can mean heavy runtime overhead and larger bundles. Tamagui resolves this with `@tamagui/static`, an optimizing compiler that: Here is what that looks like in code: The `Text` became a `p` and the `YStack` became a `div`. On native the same pass produces `Text` and `View`. Flattening is where most of the speed comes from. Across real apps around a third to a half of components flatten, and the result on web is close to hand-written CSS. On native the output is within a few percent of hand-optimized React Native, with the whole feature set intact. Everything still works at runtime. The compiler is an optimization you can add later, and a component it cannot optimize simply renders the normal way. See [Benchmarks](/docs/intro/benchmarks) for numbers and [Compiler installation](/docs/intro/compiler-install) to turn it on. ## Where to go next - [Installation](/docs/intro/installation) sets up a project or hands the job to a coding agent. - [Components](/docs/intro/components) covers `html.*`, `View`, `Text`, and how they render on each platform. - [Styling](/docs/intro/styles) explains style props, conditional values, and states. - [Design system](/docs/intro/design-system) introduces tokens, themes, and fonts. - [Tailwind](/docs/core/tailwind) covers the utility class frontend. #### Highlights --- ## Community Join us on: - [Discord](https://discord.gg/4qh6tdcVDa) - [X](https://x.com/tamagui_js) - [GitHub](https://github.com/tamagui/tamagui) --- ## Credits A big thanks to: - [Stitches](https://stitches.dev) for the variants pattern. - [JSXStyle](https://github.com/jsxstyle/jsxstyle) for providing the original version of the compiler. - [Modulz](https://github.com/modulz) for Radix, which inspired many of our component APIs, and for the initial structure of this website. - [Framer Motion](https://github.com/framer/motion) for the AnimatePresence implementation. ## intro/tokens --- title: Tokens description: The default Tamagui token scale and sizing structure --- These are the default `@tamagui/config` tokens that power this site. Your app can define its own scale and naming system. ### Understanding the size scale Most design systems use pure exponential scaling (2, 4, 8, 16). Tamagui instead uses a hybrid scale: gradual increments from 1 to 10 for fine control across common UI controls, followed by exponential steps from 11 to 16 for large display elements. This gives components a practical range of sizes where small differences matter most. #### Shared scale keys Token groups (size, space, radius) and font groups share the same 1 through 16 keys. A size 2 Button naturally aligns with size 2 padding, a size 2 icon, and size 2 text. #### Sub-unit scale (0.25, 0.5, 0.75) While the primary scale starts at 1, Tamagui includes three fractional tokens below 1 for fine adjustments, borders, and sub-pixel alignment. #### Primary scale (1-10) Sizes 1 through 10 increase gradually for controls and containers. Size 2 suits compact UI elements, size 4 is the standard default size, sizes 5 and 6 provide emphasized controls, and sizes 7 and above serve larger call-to-action blocks. #### Display scale (11-16) Sizes 11 through 16 switch to exponential spacing for hero sections and display banners. ## intro/installation --- title: Installation description: Get Tamagui set up, step by step --- Tamagui installs with no bundler configuration on web or native. Start small, and add the compiler and bundler plugins once the basics work. The fastest start is a starter template: ```bash npm create tamagui@latest ``` ## Requirements - **React 19+** - **TypeScript 5+** - **React Native 0.81+** with the New Architecture enabled, for native apps. Tamagui uses newer style features such as `boxShadow` and `filter`. On web there are no version restrictions. ## Install Install the full UI kit: ```bash yarn add tamagui @tamagui/config ``` Or install only the style library. The `tamagui` package is a superset of `@tamagui/core`, so anywhere the docs reference core you can use either: ```bash yarn add @tamagui/core @tamagui/config ``` ## Choose a styling frontend Tamagui has two ways to write styles. Both produce the same output, and the choice is per import path, so you can mix them later. Pick one to start. Style props Tailwind Typed style props on `View`, `Text`, and every other Tamagui component. This is the default for new apps. It reads like React Native and needs no extra package. ```tsx fileName="App.tsx" import { TamaguiProvider, View, createTamagui } from 'tamagui' import { defaultConfig } from '@tamagui/config/v6' const config = createTamagui(defaultConfig) declare module 'tamagui' { interface TamaguiCustomConfig extends typeof config {} } export default () => ( ) ``` Continue with [Components](/docs/intro/components) and [Styling](/docs/intro/styles). Tailwind utility classes on `className`, extracted to atomic CSS on web and resolved to native styles on iOS and Android. ```bash yarn add @tamagui/tailwind ``` ```tsx fileName="App.tsx" import { TamaguiProvider, createTamagui } from 'tamagui' import { View } from '@tamagui/tailwind' import { defaultConfig } from '@tamagui/config/v6' const config = createTamagui(defaultConfig) declare module 'tamagui' { interface TamaguiCustomConfig extends typeof config {} } export default () => ( ) ``` Continue with [Tailwind](/docs/core/tailwind). You usually export the config from a `tamagui.config.ts` file. See [Configuration](/docs/core/configuration) for what goes in it. Set your root theme with the `defaultTheme` prop on `TamaguiProvider` rather than wrapping the app in ``. That keeps the [`fastSchemeChange` setting](/docs/core/settings) working on native and lets light and dark switch through media queries during SSR on web. That is the whole setup: ```tsx class=preview line=5 import { Button } from 'tamagui' export default function Demo() { return } ``` Every feature works at runtime, so nothing above depends on a bundler plugin. When you are ready for production, [set up the compiler](/docs/intro/compiler-install) for smaller bundles and faster renders. ## Bundler guides Tamagui itself needs no bundler setup. React Native and React Native Web tooling often does, so Tamagui ships plugins that handle it: ## Next steps - **[CLI](/docs/core/cli)** for building, checking versions, and generating agent prompts. - **[Design system](/docs/intro/design-system)** for tokens, themes, and fonts. - **[Custom UI package](/docs/guides/design-systems)** to build your own optimized component library. ## intro/components --- title: Components description: The elements you build with, and what they render on each platform --- Every Tamagui component takes style props. This page covers the base elements those props go on, and which one to use when. ## View and Text `View` and `Text` are the two primitives everything else is built on. They match React Native's `View` and `Text` and add the Tamagui style props on top: ```tsx import { View, Text } from 'tamagui' export const Hello = () => ( Hello The same layout on iOS, Android, and web. ) ``` Use them by default. A `View` is a flex column on every platform, so a layout you build once looks the same on web and native. On web they render a `div` and a `span`, and you can change the tag with [`render`](/docs/core/styled#render) when you need semantic HTML, such as ``. See [View & Text](/docs/core/view-and-text) for the full prop reference, including event handlers and the non-style props Tamagui adds. ## Reusable components with styled() `styled()` wraps any of the above into a named component with default styles and typed variants. Inline props and `styled()` are both optimized by the compiler, so pick whichever reads better: ```tsx import { styled, View } from 'tamagui' export const Card = styled(View, { padding: '4', borderRadius: 'lg', backgroundColor: 'background', variants: { elevated: { true: { boxShadow: '0 4px 12px shadow-3' }, }, } as const, }) // ``` See [styled()](/docs/core/styled) and [Variants](/docs/core/variants). ## Themes in the tree `Theme` switches the theme for a subtree, and any component takes a `theme` prop for the same thing at one level: ```tsx import { Button, Theme } from 'tamagui' export const Panel = () => ( ) ``` See [Theme](/docs/core/theme) for `ThemeUpdate`, custom variables, and how sub-themes resolve. ## The UI kit The `tamagui` package adds styled, accessible components on top of these primitives: `Button`, `Dialog`, `Sheet`, `Select`, and the rest of the [UI kit](/ui/intro). Every one of them accepts `unstyled` to drop its default styles and keep only its behavior. ## HTML elements The `html` export has an element for each semantic HTML tag, such as `html.article`, `html.h1`, and `html.p`. On web each one renders that tag, and on native it renders the matching React Native primitive. It follows web and [React Strict DOM](https://facebook.github.io/react-strict-dom/) semantics, which makes it a good fit for screens that are mostly web content, like docs, marketing pages, and articles. ```tsx import { html } from 'tamagui' export const Article = () => ( A semantic heading Renders an article, h1, and p on web. ) ``` `html.*` is in early development. Its elements keep the browser's default `display`, so `html.div` is `display: block` on web, while React Native only supports flex layout. A layout built from `html.*` can therefore look different on native. Use `View` and `Text` when the same screen has to look the same everywhere. See [HTML elements](/docs/core/html-primitives) for the element list and native rules. ## Prefer utility classes? The same elements exist in `@tamagui/tailwind`, styled with `className` instead of props. See [Tailwind](/docs/core/tailwind). ## intro/compiler-install --- title: Tamagui Compiler description: Adding the compiler to your apps --- The Tamagui Compiler significantly improves performance of both web and native applications through partial analysis and view flattening. See the [Benchmarks](/docs/intro/benchmarks) or read the [in-depth background](/docs/intro/introduction#why-a-compiler). Note that Tamagui features work at compile-time and runtime, so installing the compiler is optional, and in fact we recommend only setting it up once you're ready for production. The compiler uses Babel to analyze JSX and `styled` functions, then attempts to statically analyze and optimize them down to platform-native primitives. The end result is less abstraction, such as a `div` on web or a plain React Native `View` on native:

The compiler generates built versions of your components and config into a `.tamagui` directory. You'll want to add that directory to your `.gitignore`. ## Configuration with tamagui.build.ts We recommend creating a `tamagui.build.ts` file in your project root as the single source of truth for your compiler configuration. All bundler plugins and the CLI automatically read from this file, so you only need to define your options once. This file lets the Tamagui CLI read your config and perform operations like generating CSS, pre-compiling components, and verifying optimizations, while also sharing that same configuration with whichever bundler plugin you use. Without it, you'd need to duplicate options across your metro, babel, vite, or webpack configs. ```ts fileName="tamagui.build.ts" import type { TamaguiBuildOptions } from 'tamagui' export default { config: './tamagui.config.ts', components: ['tamagui'], outputCSS: './public/tamagui.generated.css', // optional: importsWhitelist: ['constants.js', 'colors.js'], disableExtraction: process.env.NODE_ENV === 'development', } satisfies TamaguiBuildOptions ``` With this file in place, your bundler plugins can be configured with no options at all, because they pick up everything from `tamagui.build.ts`: ```tsx // vite: tamaguiPlugin() // webpack: new TamaguiPlugin() // metro: withTamagui(config) // all read from tamagui.build.ts automatically ``` You can still pass options directly to a plugin, and they'll be merged with (and override) the options from `tamagui.build.ts`. ### Experimental native theme fast path V3 beta includes an opt-in native theme engine for React Native 0.82 and newer. It can update eligible Fabric views through a ShadowTree commit without re-rendering each view in React. The API and compiler output are experimental. Install the native engine and Nitro runtime: ```sh bun add @tamagui/native-registry react-native-nitro-modules ``` Enable its compiler output in `tamagui.build.ts`: ```ts import type { TamaguiBuildOptions } from 'tamagui' export default { config: './tamagui.config.ts', components: ['tamagui'], experimental: { nativeFastPath: true, }, } satisfies TamaguiBuildOptions ``` Install the engine before your themed application tree mounts: ```ts import * as nativeRegistry from '@tamagui/native-registry' import { setNativeStyleEngine } from '@tamagui/core' if (nativeRegistry.isAvailable()) { setNativeStyleEngine(nativeRegistry) } ``` Rebuild the iOS and Android applications after adding the packages. The flag defaults to `false`. When it is off, native compiler output is unchanged. When the compiler flag is on but the engine is unavailable, components use the existing theme hook and React render path. Web output is unchanged. The registry keeps the existing `DynamicColorIOS` optimization in place. It does not replace that path. ## Install The recommended install is one package. `@tamagui/cli` carries the CLI and the bundler integrations together, each behind a subpath: ```bash yarn add -D @tamagui/cli ``` | Import | Gives you | | -------------------- | ------------------------------------------- | | `@tamagui/cli/vite` | the same exports as `@tamagui/vite-plugin` | | `@tamagui/cli/metro` | the same exports as `@tamagui/metro-plugin` | One dependency means one version to keep in sync, so the CLI and your bundler plugin can't drift apart on an upgrade. You can still install the standalone plugin instead, and if your project only ever uses one bundler that is the smaller install. Both packages stay published and the exports are identical, so the setup below is the same either way. There are plugins for a variety of bundlers, or you can use the `@tamagui/cli` to compile in-place: ### Webpack ```bash yarn add 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: ```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', } module.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 from exclude: (path) => shouldExclude(path, __dirname, 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. If you have a `tamagui.build.ts`, you can pass no options: ```tsx const { TamaguiPlugin } = require('tamagui-loader') module.exports = { plugins: [ // reads from tamagui.build.ts automatically new TamaguiPlugin(), ], } ``` Or pass options inline to override: ```tsx 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](/docs/guides/vite) for more complete setup. The Vite plugin is ESM-only. Your project must have `"type": "module"` in its `package.json` (or use `.mjs`/`.mts` config files). Update your `vite.config.ts`. If you have a `tamagui.build.ts`, no options are needed: ```tsx import { tamaguiPlugin } from '@tamagui/cli/vite' export default defineConfig({ plugins: [ // reads from tamagui.build.ts automatically tamaguiPlugin(), ], }) ``` If you installed `@tamagui/vite-plugin` on its own, import from there instead. Or pass options inline: ```tsx import { tamaguiPlugin } from '@tamagui/cli/vite' export default defineConfig({ plugins: [ tamaguiPlugin({ config: 'src/tamagui.config.ts', components: ['tamagui'], disableExtraction: true, }), ], }) ``` ### Next.js See the [guide](/docs/guides/next-js) for more complete setup. Next.js with Turbopack (the default) works best with the CLI approach: create a `tamagui.build.ts` and use `tamagui build` to optimize production builds. No bundler plugin needed. For older Webpack-based Next.js setups, add `@tamagui/next-plugin` and configure your `next.config.js`: ```js const { withTamagui } = require('@tamagui/next-plugin') module.exports = function (name, { defaultConfig }) { const tamaguiPlugin = withTamagui({ // reads from tamagui.build.ts automatically, or pass inline: config: './tamagui.config.ts', components: ['tamagui'], disableExtraction: process.env.NODE_ENV === 'development', excludeReactNativeWebExports: ['Switch', 'ProgressBar', 'Picker'], }) return { ...defaultConfig, ...tamaguiPlugin(defaultConfig), } } ``` 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](/docs/guides/next-js) for more details on setting up your app. ### Metro The Metro integration runs the same shared compiler used by the other bundler adapters after your configured Babel transformer. Wrap your existing Metro config. With a `tamagui.build.ts`, you can omit the inline options: ```js const { getDefaultConfig } = require('expo/metro-config') const { withTamagui } = require('@tamagui/cli/metro') module.exports = withTamagui(getDefaultConfig(__dirname)) ``` If you installed `@tamagui/metro-plugin` on its own, require from there instead. Or pass options inline: ```js const { getDefaultConfig } = require('expo/metro-config') const { withTamagui } = require('@tamagui/cli/metro') module.exports = withTamagui(getDefaultConfig(__dirname), { components: ['tamagui'], config: './tamagui.config.ts', logTimings: true, disableExtraction: process.env.NODE_ENV === 'development', }) ``` ### Expo [Check out the Expo guide](/docs/guides/expo) for more information on setting up Expo. It uses the same Metro integration shown above. ### CLI-Based In-Place Compilation For bundlers that don't have a Tamagui plugin yet (like Turbopack), or if you prefer a simple setup, you can use `@tamagui/cli` to pre-compile your components in-place before your build step. This approach is meant for **production builds only** and should run in your deployment pipeline, not during development. It rewrites files in place which will mess up your working directory, but makes it highly compatible with any bundler or tool. The downside is you don't get the helpful development compatibility parts of the plugins, plus dev-mode debugging and `data-` attributes. For complete CLI documentation including all available commands, see the [CLI Guide](/docs/core/cli). #### Setup 1. Install: ```bash yarn add -D @tamagui/cli ``` 2. Create a `tamagui.build.ts` if you haven't already (see [above](#configuration-with-tamaguibuildts)). 3. Add a build script to your `package.json`: ```json { "scripts": { "build": "tamagui build ./src -- next build" } } ``` #### Usage ```bash # Build all components in a directory (web + native by default) npx tamagui build ./src # Build for web only npx tamagui build --target web ./src # Build for native only npx tamagui build --target native ./src # Build a specific file npx tamagui build ./src/components/MyComponent.tsx # Include/exclude patterns npx tamagui build --include "components/**" --exclude "**/*.test.tsx" ./src # Output to a separate directory (source files unchanged) npx tamagui build --output ./dist ./src # Create platform-specific files next to source files (.web.tsx or .native.tsx) npx tamagui build --target native --output-around ./src # Preview changes without writing files npx tamagui build --dry-run ./src # Verify minimum optimizations (useful in CI) npx tamagui build --target web --expect-optimizations 10 ./src ``` #### CI Verification with --expect-optimizations The `--expect-optimizations` flag ensures your build is actually optimizing components. This is useful in CI to catch configuration issues: ```json { "scripts": { "build": "tamagui build --target web --expect-optimizations 10 ./src -- next build" } } ``` If the compiler produces fewer than the expected number of optimizations, the build will fail with an error message showing the actual count. This helps catch: - Misconfigured `components` array - Wrong source paths - Configuration files not being found #### Platform-Specific File Handling The CLI automatically handles platform-specific files (`.web.tsx`, `.native.tsx`, `.ios.tsx`, `.android.tsx`): - Files with `.web.tsx` extensions are optimized for web only - Files with `.native.tsx`, `.ios.tsx`, or `.android.tsx` extensions are optimized for native only - Base files (`.tsx`) without platform-specific versions are optimized for all platforms - If both `.web.tsx` and `.native.tsx` exist, the base `.tsx` file is skipped #### Package.json Exports Support The CLI supports `package.json` exports for path-specific imports. For example: ```json { "exports": { ".": "./src/index.tsx", "./components/Button": "./src/Button.tsx" } } ``` Both import styles work: ```tsx import { Button } from '@my/ui' import { Button } from '@my/ui/components/Button' ``` #### Integration Examples This works with **any build tool**: run `tamagui build` before your build command. Here are some examples: **Next.js with Turbopack** (Turbopack doesn't support plugins yet): ```json { "scripts": { "dev": "next dev --turbopack", "build": "tamagui build --target web ./src -- next build" } } ``` **Vite, Remix, or any other bundler:** ```json { "scripts": { "build": "tamagui build --target web ./src -- vite build" } } ``` **React Native / Expo:** ```json { "scripts": { "build:ios": "tamagui build --target native ./src -- eas build --platform ios", "build:android": "tamagui build --target native ./src -- eas build --platform android" } } ``` **Using --output (no file restoration needed):** If you prefer to output optimized files to a separate directory instead of modifying source files in-place, use `--output`: ```json { "scripts": { "build": "tamagui build --target web --output ./dist ./src && next build" } } ``` With `--output`, your source files are never modified. The optimized files are written to the output directory with their directory structure preserved. **Using --output-around (platform-specific files):** The `--output-around` flag creates optimized platform-specific files (`.web.tsx` or `.native.tsx`) next to your source files instead of modifying them. Bundlers automatically pick up these files via platform-specific resolution: ```json { "scripts": { "prebuild:native": "tamagui build --target native --output-around ./src", "prebuild:web": "tamagui build --target web --output-around ./src" } } ``` This transforms `Button.tsx` → creates `Button.native.tsx` or `Button.web.tsx` alongside it. Metro/Expo uses `.native.tsx` on native, and web bundlers use `.web.tsx`. The Tamagui CLI optimizes your components in-place (or to an output directory), then your bundler processes the already-optimized files. **Learn more:** See the [CLI Guide](/docs/core/cli) for documentation on all CLI commands including `check`, `generate`, `add`, and more. ## Props All compiler plugins accept the same options: ### Component discovery The compiler resolves every component it meets. Components defined with `styled()` anywhere in your app are linked across files, and a component imported from any package is evaluated the first time a file uses it as a JSX element or as a `styled()` base. The result is cached for the life of the dev server or build, and a package bump invalidates the cached plans that lowered against it. `components` is an optional warm-up list. Listing a package evaluates it once when the project loads instead of on first use, which is worth doing for the package most of your files import from. Nothing else is needed for the compiler to optimize components from other packages. A package that cannot run in Node during the build (one that touches native modules at import time, for example) is skipped with a debug message, and the elements that use it stay on the runtime path. Set `DEBUG=tamagui` to see which modules discovery skipped and why. ### Disabling the compiler You can disable the compiler optimizations for an entire file with a comment at the top of your file: ```tsx // tamagui-ignore ``` You can disable the compiler optimization for a single component with the boolean property `disableOptimization`: ```tsx import { html } from '@tamagui/core' export default () => ``` ## intro/themes --- title: Themes description: Create themes and sub-themes --- Themes map neatly to CSS variables: they are objects whose values you want to contextually change at any point in your React tree. Tamagui lets you define, nest, and read them on web and native. Bare theme names are the first lookup for style values, and `useTheme` exposes them directly. Themes nest both in the definition and at runtime, and values resolve upward, ultimately all the way back to tokens. For the recommended theme setup, see the [Config v6 docs](/docs/core/config-v6). See the [ThemeBuilder guide](/docs/guides/theme-builder) for generating custom theme suites. To style `bento` or `tamagui` components, see [Styling `tamagui` UI components](#styling-tamagui-components). For a copy-paste custom palette and recipe tree, see the [v6 Colors guide](/docs/core/config-v6-colors#add-a-custom-palette). You define a theme like this: ```tsx const dark = { background: '#000', color: '#fff', // define any key to any string or number value } ``` If you use tokens, you can share values from tokens down to themes. Tokens act as fallback values for themes, like global CSS variables vs scoped ones: ```tsx const tokens = createTokens({ color: { black: '#000', white: '#fff', }, }) // theme: const dark = { background: tokens.color.black, color: tokens.color.white, } ``` ### Sub-themes Tamagui supports theme nesting. Define sub-themes using the `parentName_subName` format, where each segment resolves as a valid theme. Sub-themes can nest across multiple levels: - `dark_green_subtle` - `light_green_subtle` ```tsx ``` You can also access a specific sub-theme more specifically: ```tsx ``` ### Inverse themes In v3, `inverse` is a real sub-theme generated by the default theme setup. Use it like any other sub-theme: ```tsx ``` The old `themeInverse` prop and `` path are removed. Because `inverse` is a named sub-theme, SSR can emit the right CSS variables without client-only light/dark inversion. See the [theme creation guide](/docs/guides/theme-builder#function-children) for custom theme generation details. ### Forcing a scheme with black and white `inverse` is relative: it flips whichever scheme its parent happened to be. When you need a specific scheme instead, use `black` or `white`. They resolve to the dark and light themes from any parent, including from inside a palette sub-theme: ```tsx // dark, wherever this is mounted // still dark, even though the parent is light and red ``` Reach for them when a subtree cannot know which scheme it is mounted under, such as a menu that always reads as dark over a light page. Levels nest inside them as usual, so `` steps within the dark scheme rather than falling back to the parent's. Keep themes consistent in shape, sharing the same named keys and typed values. Sub-themes can define subsets of parent themes. The `useTheme` hook and style system resolve missing keys upward through parent themes and back to tokens. #### Component themes V3 does not select themes from styled component display names. The `displayName` option sets React's debugging identity and may add an `is_Circle` class, but it does not make Tamagui search for `light_Circle` or affect style resolution. Use a normal theme boundary when a component owns a theme operation: ```tsx import { Theme, View, styled } from 'tamagui' const CircleFrame = styled(View, { displayName: 'Circle', backgroundColor: 'background', }) function Circle(props) { return ( ) } ``` This composes under light, dark, color, inverse, and existing level themes without a second component-specific lookup system. --- ### Styling Tamagui Components The `tamagui` component suite uses standard semantic theme keys across all components: - `background`: component surface color - `color`: text and icon color - `border-color`: border color - `shadow-color`: elevation and shadow color - `placeholder-color`: placeholder text color Each key supports interactive pseudo-states: `background-hover`, `background-press`, and `background-focus`. Defining these standard keys lets you re-theme both built-in UI components and your own components consistently across light and dark mode. A minimal theme might look like this: ```tsx const dark = { // Standard keys for all components background: '#000', 'background-hover': '#111', 'background-press': '#222', 'background-focus': '#333', color: '#fff', 'color-hover': '#eee', 'color-press': '#ddd', 'color-focus': '#ccc', 'border-color': '#555', 'border-color-hover': '#666', 'border-color-focus': '#777', 'border-color-press': '#888', 'placeholder-color': '#999', 'outline-color': '#aaa', // Custom tokens like "brand" 'brand-background': '#000', // You can add your own tokens like "brand" 'brand-color': '#fff', // and use them in your components } const light = { // Standard keys for all components background: '#fff', 'background-hover': '#f5f5f5', 'background-press': '#e0e0e0', 'background-focus': '#d5d5d5', color: '#000', 'color-hover': '#111', 'color-press': '#222', 'color-focus': '#333', 'border-color': '#444', 'border-color-hover': '#555', 'border-color-focus': '#666', 'border-color-press': '#777', 'placeholder-color': '#888', 'outline-color': '#999', // Custom tokens like "brand" 'brand-background': '#000', // You can add your own tokens like "brand" 'brand-color': '#fff', // and use them in your components } ``` You can of course do all of this yourself in your own design system with `styled`: If you are building a component with more than one sub-component, you can follow this pattern: ```tsx import { GetProps, View, Text, createStyledHOC, styled } from 'tamagui' // or '@tamagui/core' const ButtonFrame = styled(View, { displayName: 'Button', backgroundColor: 'background', }) const ButtonText = styled(Text, { displayName: 'ButtonText', color: 'color', }) type ButtonProps = GetProps // createStyledHOC wraps a functional component so it can be further styled with styled() // see /docs/core/styled#createstyledhoc for full documentation export const Button = createStyledHOC( ButtonFrame, ({ children, ...props }: ButtonProps, ref) => { return ( {children} ) } ) ``` The frame and text share the active theme. Wrap the composed component in an explicit named or relative level theme when it needs a different semantic surface. ## Full Example Let's start with an example of inline styling with a subset of the configuration: ```tsx import { TamaguiProvider, createTokens, createTamagui, html, Theme } from 'tamagui' const tokens = createTokens({ color: { darkRed: '#550000', lightRed: '#ff0000', }, // ... see configuration docs for required tokens }) const config = createTamagui({ tokens, themes: { dark: { red: tokens.color.darkRed, }, light: { red: tokens.color.lightRed, }, }, }) export const App = () => ( ) ``` In this example we've set up darkRed and lightRed variables and a dark and light theme that use those variables. Tamagui will handle defining: ```css :root { --colors-dark-red: #550000; --colors-light-red: #ff0000; } .tui_dark { --red: var(--colors-dark-red); } .tui_light { --red: var(--colors-light-red); } ``` Which will automatically apply at runtime, or can be gathered for use in SSR with `config.getCSS()`. Finally, the compiler on web will extract your views roughly as so: ```tsx export const App = () => (
) // CSS output: // .color-2nesi3 { background-color: var(--red); } ``` ## Ensuring valid types This structure keeps everything typed. Keep themes in a separate `themes.ts` file, and structure it like this: ```tsx import { tokens } from './tokens' const light = { background: '#fff', 'background-hover': tokens.color['gray-100'], 'background-press': tokens.color['gray-200'], 'background-focus': tokens.color['gray-300'], 'border-color': tokens.color['gray-200'], 'border-color-hover': tokens.color['gray-400'], color: tokens.color['gray-950'], 'color-hover': tokens.color['gray-800'], 'color-press': tokens.color['gray-700'], 'color-focus': tokens.color['gray-400'], 'shadow-color': 'rgba(0, 0, 0, 0.12)', } // note: we set up a single consistent base type to validate the rest: type BaseTheme = typeof light // the rest of the themes use BaseTheme const dark: BaseTheme = { background: '#000', 'background-hover': tokens.color['gray-900'], 'background-press': tokens.color['gray-800'], 'background-focus': tokens.color['gray-700'], 'border-color': tokens.color['gray-800'], 'border-color-hover': tokens.color['gray-700'], color: '#ddd', 'color-hover': tokens.color['gray-100'], 'color-press': tokens.color['gray-200'], 'color-focus': tokens.color['gray-400'], 'shadow-color': 'rgba(0, 0, 0, 0.25)', } const dark_translucent: BaseTheme = { ...dark, background: 'rgba(0,0,0,0.7)', 'background-hover': 'rgba(0,0,0,0.5)', 'background-press': 'rgba(0,0,0,0.25)', 'background-focus': 'rgba(0,0,0,0.1)', } const light_translucent: BaseTheme = { ...light, background: 'rgba(255,255,255,0.85)', 'background-hover': 'rgba(250,250,250,0.85)', 'background-press': 'rgba(240,240,240,0.85)', 'background-focus': 'rgba(240,240,240,0.7)', } export const allThemes = { dark, light, dark_translucent, light_translucent, } satisfies { [key: string]: BaseTheme } ``` ## Dynamic Themes Sometimes you want to defer loading themes, or change existing theme values at runtime. Tamagui exports three helpers for this in the package `@tamagui/theme` which exports `addTheme`, `updateTheme`, and `replaceTheme`. ### addTheme ```tsx hero template=AddTheme ``` ### updateTheme ```tsx hero template=UpdateTheme ``` ### replaceTheme ```tsx hero template=ReplaceTheme ``` ### Notes - Dynamic themes only work on the client side and will be ignored on the server side. - The difference between `updateTheme` and `replaceTheme` is that `replaceTheme` will replace the entire theme, while `updateTheme` will only update the values that are passed in. ### Advanced Optimization To omit theme objects from a server-rendered web app's client bundle, follow [Tree shaking themes](/docs/core/tamagui-provider#tree-shaking-themes). That section covers the CSS and bundler setup required before hydration. ## intro/static --- title: Static Optimization description: How @tamagui/static optimizes code at build time --- The Tamagui compiler turns component styles into atomic CSS and flattened platform primitives at build time. For details on how the compiler optimizes your code through static analysis: - [Why a compiler](/docs/intro/introduction#why-a-compiler): architectural background on compiler benefits - [Compiler Installation](/docs/intro/compiler-install): how to set up the compiler in your project - [Benchmarks](/docs/intro/benchmarks): performance measurements across platforms ## intro/errors --- title: Errors description: Common error messages and how to resolve them --- Find an error number below to see its cause and the steps that resolve it. ### Error 001 Haven't called createTamagui yet. This often happens due to having duplicate Tamagui sub-dependencies. Tamagui needs every `@tamagui/*` dependency to be on the exact same version. Starter kits include an upgrade script you can run with `yarn upgrade:tamagui` to align package versions. You can also remove `node_modules` and run a fresh install after upgrading. ### Error 002 Using global config fallback. This may indicate duplicate tamagui instances (e.g., from Vite SSR bundling). This is handled automatically, but may cause issues due to duplicate tamagui modules if ignored. ### Error 003 #### Static evaluation failed The compiler evaluates your Tamagui config and configured component modules in Node during the build. The error names the module that could not be evaluated, the importing file, and the original failure. Fix that failure so the compiler can read the same config and component metadata as the application. If the module is only used at runtime and none of its exports create your Tamagui config or components, ignore that module explicitly in `tamagui.build.ts`: ```ts export default { components: ['tamagui'], dangerouslyIgnoreStaticEvaluationModules: ['runtime-only-package'], } ``` Ignored modules receive an empty object during static evaluation. Do not ignore a module whose exports contribute to `createTamagui()` or a configured component. ### Error 004 #### Flat value clause condition depth exceeded A flat value clause supports at most 5 non-platform conditions (e.g. `hover:sm:focus:dark:`). Reduce the number of chained modifiers on a single clause. ### Error 005 #### Invalid variable name `createCSSVariable` expected a string variable name. ### Error 006 #### Missing component in styled() `styled()` was called without a target component. Pass a valid React component or host element tag. ### Error 007 #### createTamagui in zero-runtime graph `createTamagui` was invoked in a zero-runtime graph. In zero-runtime mode, config parsing and CSS generation happen at build time. Remove client config references or mark the module as full-runtime. ### Error 008 #### Reserved token name collision A token name collides with a reserved CSS-wide keyword (`initial`, `inherit`, `unset`, etc.). Rename the token to avoid collision with standard CSS identifiers. ### Error 009 #### Missing default font `settings.defaultFont` points to a font family that was not defined in the `fonts` configuration. ### Error 010 #### createComponent in zero-runtime graph A Tamagui component renderer survived compilation in a zero-runtime bundle. Move the component to a declared full-runtime island or ensure static compilation extracts it. ### Error 011 #### Missing animation driver No animation driver was configured. Pass an `animations` driver to `createTamagui()`. ### Error 012 #### Missing animated-number hooks in driver The configured animation driver does not support animated numbers. For CSS animations, use `@tamagui/animations-css/extras`. ### Error 013 #### Invalid matchMedia implementation `matchMedia` did not return a valid `MediaQueryList` object. Supply a compatible `matchMedia` polyfill on native platforms. ### Error 014 #### Missing parent theme for shallow render `` requires a mounted parent `` component to derive its scoped theme hierarchy. ### Warning 002 You're rendering a Tamagui component without nesting it inside a parent that is able to adapt. Adapt must be a child of a Dialog, Popover or Select. If that parent uses a `scope` prop, the Adapt needs the matching adapt scope. See [Adapt](/ui/adapt). ## intro/colors --- title: Colors description: Color palettes and customization options --- Explore the default color palettes and learn how to configure custom palettes with `@tamagui/colors` or Config v6. Tamagui defaults to Tailwind-aligned colors in `@tamagui/config/v6`, while also supporting Radix palettes via `@tamagui/colors`. For configuring custom color palettes and recipe-generated adaptive themes, see the [v6 Colors guide](/docs/core/config-v6-colors). ## intro/design-system --- title: Design system description: Tokens, themes, fonts, media queries, and how they reach your styles --- A Tamagui config is a small design system. Style props resolve their values against it, so `padding="4"` and `color="color-11"` mean the same thing on every screen and every platform. Most apps start from the `@tamagui/config/v6` preset and customize from there. It ships Tailwind's spacing scale, the full Tailwind palette, adaptive light and dark themes, and Tailwind-aligned shorthands: ```tsx fileName="tamagui.config.ts" import { defaultConfig } from '@tamagui/config/v6' import { createTamagui } from 'tamagui' export const config = createTamagui(defaultConfig) declare module 'tamagui' { interface TamaguiCustomConfig extends typeof config {} } ``` See [Config v6](/docs/core/config-v6) for what the preset contains and [Configuration](/docs/core/configuration) for building one from scratch. ## Tokens Tokens are named static values. They become CSS variables on web and plain values on native, and every style prop accepts a token from the matching category: | Category | Style props it feeds | v6 example | | --- | --- | --- | | `space` | padding, margin, gap, inset | `padding="4"` is 16px | | `size` | width, height, min and max sizes | `width="64"` | | `radius` | borderRadius and its corners | `borderRadius="lg"` | | `color` | any color prop | `backgroundColor="blue-500"` | | `zIndex` | zIndex | `zIndex="1"` | A bare value such as `"4"` looks up the token for that prop's category. Numbers are pixels, and `"4px"` or `px(4)` forces a pixel value when a token has the same name. See [Tokens](/docs/core/tokens). ## Themes Themes are values that change per subtree, most often colors. A theme is an object of keys, and sub-themes named `parent_child` fall back to their parent: ```tsx themes: { light: { background: '#fff', color: '#000' }, dark: { background: '#000', color: '#fff' }, dark_blue: { background: '#0b1a33', color: '#e5efff' }, } ``` Style props read theme keys by name, so `backgroundColor="background"` follows whichever theme is active. The v6 preset adds an adaptive ramp, `color-1` through `color-11`, that steps from the page background toward the strongest foreground in both schemes: ```tsx Adapts to light and dark ``` Switch themes with `` or a `theme` prop. See [Themes](/docs/intro/themes) for sub-themes, component themes, and inverse themes, and [Creating themes](/docs/guides/theme-builder) to generate full suites. ## Fonts Fonts are tokens with structure. Each family carries its own size, line height, weight, and letter spacing scales, so `fontSize="lg"` and `lineHeight="lg"` pull from the same family. Text components pick a family with `fontFamily`, and `SizableText`, `Paragraph`, and the headings take a `size` prop that sets all four at once: ```tsx Body copy Body copy ``` See [Fonts](/docs/core/fonts) for `createFont`, native font files, and per-language fonts. ## Media queries Named media queries in the config become modifiers on every style prop, keys on `useMedia`, and container query names: String Typed ```tsx ``` ```tsx ``` The v6 preset defines `xs`, `sm`, `md`, `lg`, `xl`, and `xxl` as min-width queries with matching `max-sm` style variants, plus `hoverable` and `touchable`. See [useMedia](/docs/core/use-media). ## Where the pieces meet ```tsx Save ``` Spacing and radius come from tokens, colors from the active theme, font size from the body font, and `md:` from the media config. The compiler turns all of it into atomic CSS on web, and the runtime resolves the same values on native. ## components/intro/1.0.0 --- title: Tamagui Components --- Tamagui Components is a complete suite of components that render nicely on both React web and React Native and come in both styled and unstyled forms. ## Install You can install each component separately, or all of them at once with: ```bash yarn add tamagui ``` ## React Strict DOM for React Native Tamagui's UI components follow the React Strict DOM model: DOM-shaped elements and props that render to real native views on native. The root `html.*` export allows a single component tree to use semantic DOM elements on web and the corresponding React Native primitives on native: - **Layout elements** (`html.div`, `html.article`, `html.main`, `html.section`, `html.ul`) render to `View` on native and literal semantic tags on web. - **Text elements** (`html.p`, `html.span`, `html.strong`, `html.h1`–`html.h6`) render to `Text` on native and matching headings/paragraphs on web. - **Media & inputs** (`html.img` maps to `Image`, `html.input` / `html.textarea` map to `TextInput`). On native, DOM props are mapped to their React Native equivalents (such as `href` on `html.a` and `src` on `html.img`), and raw strings inside layout elements are automatically wrapped. Because each member is a standard Tamagui component, style props accept tokens, themes, shorthands, and flat conditional values with compiler lowering. For more details, see the [HTML primitives documentation](/docs/core/html-primitives). ## Setup The package `tamagui` is a superset of `@tamagui/core`, so if you've already installed core you can change all the references to `tamagui`. You'll need to add a provider to the root of your app (unlike core, where that is optional), as it will set up the root portal for components like dialogs and popovers. ```tsx import { createTamagui, TamaguiProvider, View } from 'tamagui' import { defaultConfig } from '@tamagui/config/v6' // for quick config install this const config = createTamagui(defaultConfig) export default () => ( ) ``` For a full guide configuration with `createTamagui`, check [the core configuration docs](/docs/core/configuration). For native setup, platform integrations, and the fast-path runtime, see [the Native documentation](/ui/native). ## components/intro/2.0.0 --- title: Tamagui Components description: React Native UI kit with copy-paste composable components --- Tamagui Components is a complete suite of copy-paste composable components that render nicely on both React Native and React web, in both styled and unstyled forms. ## Install You can install each component separately, or all of them at once with: ```bash npm install tamagui ``` ## React Strict DOM for React Native Tamagui's UI components follow the React Strict DOM model: DOM-shaped elements and props that render to real native views on native. The root `html.*` export allows a single component tree to use semantic DOM elements on web and the corresponding React Native primitives on native: - **Layout elements** (`html.div`, `html.article`, `html.main`, `html.section`, `html.ul`) render to `View` on native and literal semantic tags on web. - **Text elements** (`html.p`, `html.span`, `html.strong`, `html.h1`–`html.h6`) render to `Text` on native and matching headings/paragraphs on web. - **Media & inputs** (`html.img` maps to `Image`, `html.input` / `html.textarea` map to `TextInput`). On native, DOM props are mapped to their React Native equivalents (such as `href` on `html.a` and `src` on `html.img`), and raw strings inside layout elements are automatically wrapped. Because each member is a standard Tamagui component, style props accept tokens, themes, shorthands, and flat conditional values with compiler lowering. For more details, see the [HTML primitives documentation](/docs/core/html-primitives). ## Setup The package `tamagui` is a superset of `@tamagui/core`, so if you've already installed core you can change all the references to `tamagui`. You'll need to add a provider to the root of your app (unlike core, where that is optional), as it will set up the root portal for components like dialogs and popovers. ```tsx import { createTamagui, TamaguiProvider, html } from 'tamagui' import { defaultConfig } from '@tamagui/config/v6' // for quick config install this const config = createTamagui(defaultConfig) export default () => ( ) ``` For a full configuration guide with `createTamagui`, check [the core configuration docs](/docs/core/configuration). For native setup, platform integrations, and the fast-path runtime, see [the Native documentation](/ui/native). ## components/anchor/1.0.0 --- title: Anchor description: Link to external websites. name: html component: Anchor --- ## Usage The Anchor component provides a way to link to external websites. It extends [SizableText](/docs/components/text#sizable-text), adding the `href`, `target`, and `rel` attributes. On native, it will use React Native `Linking.openURL`, on web it will render to an `a` element with `href` set appropriately. ## API Reference ### Anchor Inherits [Tamagui props](/docs/intro/props) as well as: ## components/anchor/2.0.0 --- title: Anchor description: Link to external websites name: anchor component: Anchor --- Anchor adds Tamagui style props and cross-platform behavior to links. ## Installation Anchor is already installed in `tamagui`: ## Usage Anchor extends [SizableText](/ui/text#sizabletext), adding the `href`, `target`, and `rel` attributes. On native, it uses React Native `Linking.openURL`. On web, it renders an `a` element with `href` set appropriately. ## API reference ### Anchor Inherits [Tamagui props](/docs/intro/props) as well as: ## components/new-inputs/1.0.0 --- title: Input & Textarea name: inputs component: Input demoName: Inputs --- ```tsx hero template=Inputs ``` Using Web APIs and relying on bare Tamagui with no `react-native-web` depedency on web compared to old Input component, support scaling all the styles up or down using the `size` property, and full `theme` support. ## Installation LinearGradient is already installed in `tamagui`, or you can install it independently: ```bash npm install @tamagui/input ``` ## Input A one-line input field: ```tsx import { Input } from 'tamagui' export const App = () => ( // Accepts size and style properties directly ) ``` ## TextArea For multi-line inputs: ```tsx import { TextArea } from 'tamagui' export const App = () => ( // Accepts size and style properties directly