# Tamagui
> description: A vertically stacked set of interactive headings with content.
---
# Source: https://tamagui.dev/ui/accordion.md
---
title: Accordion
description: A vertically stacked set of interactive headings with content.
name: accordion
component: Accordion
package: accordion
---
```tsx hero template=Accordion
```
## Installation
Accordion is already installed in `tamagui`, or you can install it independently:
```bash
npm install @tamagui/accordion
```
## Anatomy
Import all parts and piece them together.
```jsx
import { Accordion } from 'tamagui' // or '@tamagui/accordion'
export default () => (
)
```
## API Reference
### Accordion
Contains all the parts of an accordion.
Determines whether one or multiple items can be opened at the same time.
),
},
{
name: 'value',
required: false,
type: 'string',
description: (
The controlled value of the item to expand when type is{' '}
"single". Must be used in conjunction with{' '}
onValueChange.
),
},
{
name: 'defaultValue',
required: false,
type: 'string',
description: (
The value of the item to expand when initially rendered and type is{' '}
"single". Use when you do not need to control the state of the
items.
),
},
{
name: 'onValueChange',
required: false,
type: '(value: string) => void',
typeSimple: 'function',
description: (
Event handler called when the expanded state of an item changes and{' '}
type is "single".
),
},
{
name: 'value',
required: false,
default: '[]',
type: 'string[]',
description: (
The controlled value of the item to expand when type is{' '}
"multiple". Must be used in conjunction with{' '}
onValueChange.
),
},
{
name: 'defaultValue',
required: false,
default: '[]',
type: 'string[]',
description: (
The value of the item to expand when initially rendered when type{' '}
is "multiple". Use when you do not need to control the state of the
items.
),
},
{
name: 'onValueChange',
required: false,
type: '(value: string[]) => void',
typeSimple: 'function',
description: (
Event handler called when the expanded state of an item changes and{' '}
type is "multiple".
),
},
{
name: 'collapsible',
required: false,
default: 'false',
type: 'boolean',
description: (
When type is "single", allows closing content when
clicking trigger for an open item.
),
},
{
name: 'disabled',
required: false,
type: 'boolean',
default: 'false',
description: (
When true, prevents the user from interacting with the accordion
and all its items.
),
},
{
name: 'dir',
required: false,
type: '"ltr" | "rtl"',
typeSimple: 'enum',
default: '"ltr"',
description:
'The reading direction of the accordion when applicable. If omitted, assumes LTR (left-to-right) reading mode.',
},
]}
/>
### Item
Contains all the parts of a collapsible section.
When true, prevents the user from interacting with the item.
),
},
{
name: 'value',
required: true,
type: 'string',
description: 'A unique value for the item.',
},
]}
/>
### Header
Wraps an `Accordion.Trigger`. Use the `asChild` prop to update it to the appropriate heading level for your page.
### Trigger
Toggles the collapsed state of its associated item. It should be nested inside of an `Accordion.Header`.
### Content
Contains the collapsible content for an item.
Used to force mounting when more control is needed. Useful when controlling
animation with React animation libraries.
),
},
]}
/>
## Examples
### Expanded by default
Use the `defaultValue` prop to define the open item by default.
```jsx line=1
……
```
### Allow collapsing all items
Use the `collapsible` prop to allow all items to close.
```jsx line=1
……
```
### Multiple items open at the same time
Set the `type` prop to `multiple` to enable opening multiple items at once.
```jsx line=1
……
```
## Accessibility
Adheres to the [Accordion WAI-ARIA design pattern](https://www.w3.org/TR/wai-aria-practices-1.1/#accordion).
---
# Source: https://tamagui.dev/ui/alert-dialog.md
---
title: AlertDialog
description: Show an alert prompt in a dialog
name: alertDialog
component: AlertDialog
package: alert-dialog
demoName: AlertDialog
---
```tsx hero template=AlertDialog
```
## Installation
Alert Dialog is already installed in `tamagui`, or you can install it independently:
```bash
npm install @tamagui/alert-dialog
```
In order to use this component independently of `tamagui`, you'll first need to install the `@tamagui/portal` package:
```bash
npm install @tamagui/portal
```
Then add `PortalProvider` to the root of your app:
```tsx fileName="App.tsx"
import { PortalProvider } from '@tamagui/portal'
import YourApp from './components/YourApp'
function App() {
return (
)
}
export default App
```
## Anatomy
```tsx
import { AlertDialog } from 'tamagui' // or '@tamagui/alert-dialog'
export default () => (
{/* ... */}
)
```
## API Reference
### AlertDialog
Contains every component for the AlertDialog. Shares all [Dialog Props](/docs/components/dialog#api), except modal which is on by default. Adds:
### AlertDialog.Trigger
Just [Tamagui Props](/docs/intro/props).
### AlertDialog.Portal
Renders AlertDialog into appropriate container. Beyond [Tamagui Props](/docs/intro/props), adds:
### AlertDialog.Content
Main container for AlertDialog content, this is where you should apply animations.
Beyond [Tamagui Props](/docs/intro/props), adds:
### AlertDialog.Overlay
Displays behind Content. Beyond [Tamagui Props](/docs/intro/props), adds:
### AlertDialog.Title
Required. Can wrap in VisuallyHidden to hide.
Defaults to H2, see [Headings](/docs/components/headings).
### AlertDialog.Description
Required. Can wrap in VisuallyHidden to hide.
Defaults to Paragraph, see [Paragraph](/docs/components/text).
### AlertDialog.Cancel
Closes the AlertDialog, accepts all YStack props. Recommended to use with your own component and `asChild`.
### PortalProvider
## Examples
### Inside native modals
If you're using native modals (maybe from react-navigation), you'll notice the Dialogs won't show up inside the modal. To get around this, you should wrap your screen inside `PortalProvider`, like so:
```tsx
import { PortalProvider } from 'tamagui'
// this component used in react-navigation/expo-router with `presentation: "modal"`
export function Page() {
return (
{/* rest of your page, including the Dialog... */}
)
}
```
---
# Source: https://tamagui.dev/ui/anchor.md
---
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:
---
# Source: https://tamagui.dev/docs/core/animations.md
---
title: Animations
description: Swap out animation drivers per-platform or at runtime
---
```tsx hero template=Animations
```
Add animations to Tamagui with an animation driver. See the configuration docs
for more on
[how to set it up, and how to set up different animation drivers per-platform](/docs/core/configuration#animations).
Animation drivers are designed to be swappable, so you can use lightweight CSS
animations or other web-focused animation libraries on the web, while using
larger but more advanced libraries like `reanimated` on native - all without
having to change a line outside of configuration.
## Installation
### CSS
The `@tamagui/animations-css` package works with the tamagui compiler and
runtime to give you simple ways to share typed animations across all your
components.
To install it add to your package.json:
```bash
yarn add @tamagui/animations-css
```
Then add it to your [config](/docs/core/configuration):
```tsx
import { createAnimations } from '@tamagui/animations-css'
import { createTamagui } from 'tamagui'
export default createTamagui({
animations: createAnimations({
fast: 'ease-in 150ms',
medium: 'ease-in 300ms',
slow: 'ease-in 450ms',
}),
// ...
})
```
At runtime, the plugin does very little except to set the `transition` property
in CSS. At compile-time, the compiler does the same, ensuring you get all the
benefits of prop removal and view flattening even when using animations.
### React Native Animated
[React Native's Animated library](https://reactnative.dev/docs/animated) is the
animation library that comes built into React Native and React Native Web.
To install it add to your package.json:
```bash
yarn add @tamagui/animations-react-native
```
Then add it to your [config](/docs/core/configuration):
```tsx
import { createAnimations } from '@tamagui/animations-react-native'
import { createTamagui } from 'tamagui'
export default createTamagui({
animations: createAnimations({
fast: {
damping: 20,
mass: 1.2,
stiffness: 250,
},
medium: {
damping: 10,
mass: 0.9,
stiffness: 100,
},
slow: {
damping: 20,
stiffness: 60,
},
}),
// ...
})
```
### Reanimated
[Reanimated](https://docs.swmansion.com/react-native-reanimated/) is an
animation library that targets React Native and React Native Web. It runs
off-thread animations, and provides simple syntax and hooks.
To install it add to your package.json:
```bash
yarn add @tamagui/animations-moti react-native-reanimated
```
Tamagui leverages and appreciates the popular open source library [Moti](https://moti.fyi) for the Reanimated driver as it saved us many lines of complex code.
Add your animations to your [configuration](/docs/core/configuration):
```tsx
import { createAnimations } from '@tamagui/animations-moti'
import { createTamagui } from 'tamagui'
export default createTamagui({
animations: createAnimations({
fast: {
type: 'spring',
damping: 20,
mass: 1.2,
stiffness: 250,
},
medium: {
type: 'spring',
damping: 10,
mass: 0.9,
stiffness: 100,
},
slow: {
type: 'spring',
damping: 20,
stiffness: 60,
},
}),
// ...
})
```
At runtime, this plugin parses animatable style properties and hands them over
to reanimated off-thread, using worklets. It doesn't do anything at compile
time - reanimated must run via JS.
---
Note the keys match between CSS and reanimated, so you can swap them out.
The Animated driver in React Native Web can be excluded from your bundle with either the Webpack or Next.js plugins with [`excludeReactNativeWebExports`
compiler option](#excludeReactNativeWebExports).
### Usage
The `animation` can now accept `slow` as a value. By default, animations will
apply to all animatable styles, a lot like setting `all` in a CSS `transition`.
Let's test this by setting `hoverStyle`:
Here's an example animating `hoverStyle`:
```tsx hero template=AnimationsHover
```
### The animation props rules
If you add an `animation` prop, you must always keep the prop. If you need the animation to be disabled,
pass false, null or even undefined if it suits you.
The spring-based animation drivers have expensive hooks that would degrate runtime
performance if present on every component. As a workaround, the animation hooks
are called conditionally on whether the animation key is present in the props object.
So, `` rather than
``.
If you'd like to remove or add an animation prop after a component has already rendered, you'd have
to change the key.
### enterStyle
Setting `enterStyle` styles on any component tell it to start with those styles,
and after mount animate to their flat styles:
```tsx hero template=AnimationsEnter
```
### Granular animations
The `animation` prop accepts a string or a more complex object to customize
animations per-property.
The basic object we'll call an `AnimationConfig`, looks like this:
```tsx
import { YStack } from 'tamagui'
export default () => (
)
```
Note that values can either map to `AnimationKey` as a string, or to
`{ type: AnimationKey, ...configuration }`
You can set a default value using a two-arity array with the default in the
first position:
```tsx
import { YStack } from 'tamagui'
export default () => (
)
```
### animateOnly
The `animateOnly` prop will limit your animation config to certain keys. It
accepts an array of strings that correspond to style property names.
## AnimatePresence
### exitStyle
AnimatePresence animates direct children before they unmount. It's inspired by
and forked from [Framer Motion](https://github.com/framer/motion), but works
with any animation in Tamagui.
To use with `@tamagui/core`, install and import `@tamagui/animate-presence`.
It's already bundled and exported from `tamagui`.
You can use it simply with `enterStyle` + `exitStyle`:
```tsx
import { AnimatePresence, View } from 'tamagui'
export const MyComponent = ({ isVisible }) => (
{isVisible && (
)}
)
```
Note you don't even need to set `opacity` on the base style. Tamagui knows to
normalize styles like opacity and scale to 1 (and y to 0) if it's not defined
on the base styles but is defined on `enterStyle` or `exitStyle`.
Wrap one or more tamagui components with AnimatePresence. This component will
animate on enter and exit.
Animated child components must each have a unique key prop so AnimatePresence
can track their presence in the tree.
### The `custom` prop
```tsx hero template=AnimationsPresence
```
AnimatePresence also takes a `custom` property that allows you to update a
variant of the animated child before it runs it's exit animation. This is useful
for animating a child out of the screen before it unmounts in a different
direction, like the example above:
```tsx
import { AnimatePresence } from '@tamagui/animate-presence'
import { ArrowLeft, ArrowRight } from '@tamagui/lucide-icons'
import { useState } from 'react'
import { Button, Image, XStack, YStack, styled } from 'tamagui'
const GalleryItem = styled(YStack, {
zIndex: 1,
x: 0,
opacity: 1,
fullscreen: true,
variants: {
// 1 = right, 0 = nowhere, -1 = left
going: {
':number': (going) => ({
enterStyle: {
x: going > 0 ? 1000 : -1000,
opacity: 0,
},
exitStyle: {
zIndex: 0,
x: going < 0 ? 1000 : -1000,
opacity: 0,
},
}),
},
} as const,
})
const photos = [
'https://picsum.photos/500/300',
'https://picsum.photos/501/300',
'https://picsum.photos/502/300',
]
const wrap = (min: number, max: number, v: number) => {
const rangeSize = max - min
return ((((v - min) % rangeSize) + rangeSize) % rangeSize) + min
}
export function Demo() {
const [[page, going], setPage] = useState([0, 0])
const imageIndex = wrap(0, photos.length, page)
const paginate = (going: number) => {
setPage([page + going, going])
}
return (
)
}
```
### What to know when animating
#### Conditional animations and HMR
The animation hooks are heavy, which initially meant we either had to choose
great performance or animations. We've settled on a trade-off. We track if the
`animation` prop is set, and if so, we enable the hook. If it _is ever set, even
just once_, then the hooks will continue to run for the remainder of the
component lifecycle. This means if you ever plan to animate a component you
should keep `animation` always set on the component props. You can disable it
like so:
```tsx
```
Note that because of this constraint, you also will see an error if you add the
`animation` prop to a component in dev mode during an HMR. Often just saving once more will remove the screen, or reloading at worst.
---
# Source: https://tamagui.dev/ui/avatar.md
---
title: Avatar
description: Display aspect-fixed images with a fallback while loading
name: avatar
component: Avatar
package: avatar
demoName: Avatar
---
```tsx hero template=Avatar
```
## Installation
Avatar is already installed in `tamagui`, or you can install it independently:
```bash
npm install @tamagui/avatar
```
## Usage
```tsx
import { Avatar } from 'tamagui'
export default () => (
)
```
## API Reference
### Avatar
Avatar extends [Square](/docs/components/shapes#shape-props), giving it all the [Tamagui standard props](/docs/intro/props) as well as `size` and `circular`.
### Avatar.Fallback
Avatar.Fallback extends [YStack](/docs/components/stacks), plus:
### Avatar.Image
Avatar.Image extends [Image](/docs/components/image).
---
# Source: https://tamagui.dev/docs/intro/benchmarks.md
---
title: Benchmarks
description: Performance tests and comparisons
---
Tamagui compares well to the fastest libraries at runtime, and the compiler further optimizes your styled components, flattening them down and hoisting objects out of your rendering loop (and extracting CSS on the web).
With the compiler on Tamagui will output near optimal React code despite giving many nice shorthand style props. It has a higher impact on web, but with initial support for flattening more dynamic styled components now landed, Tamagui does flatten a very high % of your code on all platforms. We look forward to updating our benchmarks as we expect we've improved in a number of areas.
It's important to note that benchmarks never show a complete picture and that these benchmarks are not being actively updated as of early 2023. Our plan is to build out a better benchmarking setup to update this page.
## React Native
In [this benchmark](https://github.com/tamagui/benchmarks) Tamagui is within 10% of the speed of vanilla React Native, even when using nearly all of Tamagui's features. We render list of 28 items with a few sections, text and images. Average of 5 runs on a Apple M2 Air:
Running Tamagui without the compiler averages `125ms` or ~12% slower. Note that the compiler is much more effective on the web as it turns many more props into CSS and both bundle size and render performance are more important.
## Web
We forked and ran a few more [benchmarks](https://github.com/tamagui/tamagui/tree/24ac758bbe5d6ff2f67f25071df4286e0594f578/starters/benchmark) for the web.
**Legend:** RNW = react-native-web, SC = styled-components
### Simple component
Timing rendering a simple custom component.
### Updating variants
Changing variants is fast at runtime, and even faster when compiled:
> Since styled-components and Emotion don't offer a first-class variant API, this was done via prop interpolation.
### Updating inline styles
Tamagui has a big advantage for inline styles, it's the only library to compile them and flatten the tree.
---
### Fully dynamic styles
These benchmarks don't benefit from the compiler. The React Native API surface is much more feature-rich than DOM.
- NativeBase - couldn't get running
> Note: This test was taken from the [styled-components benchmarks](https://github.com/styled-components/styled-components/tree/main/packages/benchmarks).
### Mounting deep tree
In this test, we mount a tree with many nested nodes.
- SC - Styled Components
- NativeBase - couldn't get running
> Note: This test was taken from the [styled-components benchmarks](https://github.com/styled-components/styled-components/tree/main/packages/benchmarks).
---
# Source: https://tamagui.dev/ui/button.md
---
title: Button
description: An incredibly flexible button.
name: button
component: Button
package: button
demoName: Button
---
```tsx hero template=Button
```
## Usage
When using the simple Button API, it's as simple as this:
```tsx
import { Button } from 'tamagui'
export default () => Lorem ipsum
```
### Sizing
Sizing buttons provides a unique challenge especially for a compiler, because you need to adjust many different properties - not just on the outer frame, but on the text wrapped inside. Tamagui supports adjusting the padding, border radius, font size and icons sizes all in one with the `size` prop.
```tsx
import { Button } from 'tamagui'
export default () => Lorem ipsum
```
Given your theme defines a size `6`, the button will adjust all of the properties appropriately. You can also pass a plain number to get an arbitrary size.
### Icon Theming
You can pass icons as either elements or components. If passing components, Tamagui will automatically pass the `size` and `color` prop to them based on your theme.
You can [use the source of Button itself](https://github.com/tamagui/tamagui/blob/main/code/ui/button/src/Button.tsx) to see in more detail what variants you can override, and how we use this pattern internally to create our Button component.
### Creating your own Button
Tamagui now has all the features necessary to make creating a custom Button easy enough that you may want to roll your own button. Learn how to do it with our dedicated guide, [How to Build a Button](/docs/guides/how-to-build-a-button).
The previous `useButton` API is deprecated and will be removed in a future version. It's
brittle and is easily replaced with the new compound component APIs as described in
the guide.
## API Reference
### Button
Buttons extend Stack views inheriting all the [Tamagui standard props](/docs/intro/props), plus:
---
# Source: https://tamagui.dev/ui/card.md
---
title: Card
description: Sizable, themeable cards with a flexible API.
name: card
component: Card
package: card
demoName: Card
---
```tsx hero template=Card
```
## Installation
Card is already installed in `tamagui`, or you can install it independently:
```bash
npm install @tamagui/card
```
## Anatomy
```tsx
import { Card } from 'tamagui' // or '@tamagui/card'
export default () => (
{/* any other components */}
)
```
## API Reference
### Card
Frame of the card, extends [ThemeableStack props](/docs/components/stacks#themeablestack), adding:
### Card.Header
Extends [ThemeableStack](/docs/components/stacks#themeablestack), adding:
### Card.Footer
Extends [ThemeableStack](/docs/components/stacks#themeablestack), adding:
### Card.Background
Extends [YStack](/docs/components/stacks), set to `fullscreen` and zIndex below Header/Footer.
---
# Source: https://tamagui.dev/ui/checkbox.md
---
title: Checkbox
description: Toggle state in forms.
name: checkbox
component: Checkbox
package: checkbox
demoName: Checkbox
---
StyledUnstyledHeadless
```tsx hero template=Checkbox
````
```tsx hero template=CheckboxUnstyled
````
```tsx hero template=CheckboxHeadless
````
## Installation
Checkbox is already installed in `tamagui`, or you can install it independently:
```bash
npm install @tamagui/checkbox
````
Checkbox is already installed in `tamagui`, or you can install it independently:
```bash
npm install @tamagui/checkbox
```
To use the headless checkbox, you want to import it from the
`@tamagui/checkbox-headless` package. This package has no dependency on
`@tamagui/core`, but still works off the react-native APIs.
This means you can bring your own style library.
```bash
npm install @tamagui/checkbox-headless
```
## Usage
```tsx
import { Check } from '@tamagui/lucide-icons'
import { Checkbox } from 'tamagui'
export default () => (
)
```
Use the `createCheckbox` export to create a fully custom checkbox that still
uses the Tamagui styling system. This is similar to setting `unstyled`, but goes
a bit further. It doesn't add any types for `size` or `unstyled`, and it won't
automatically apply the `active` theme. It does pass the `checked` prop down as
indicated in the types of `createCheckbox`.
```tsx template=CheckboxUnstyled
```
Using the `useCheckbox` API, you can make your own Checkbox from scratch.
```tsx template=CheckboxHeadless
```
## API Reference
### Checkbox
`Checkbox` extend ThemeableStack inheriting all the
[props](/docs/components/stacks#themeablestack), plus:
void',
description: 'Callback that fires when the checkbox state is changed.',
},
{
name: 'sizeAdjust',
type: 'number',
description: `Adjust the checkbox size scaling by this number.`,
},
{
name: 'scaleIcon',
type: 'number',
description: `Scale the indicator icon more than usual by this number.`,
},
{
name: 'scaleSize',
type: 'number',
default: '0.5',
description: `The Tamagui size tokens should map to the height of a button at any given step. This means you want somewhat smaller checkboxes typically.`,
},
{
name: 'unstyled',
required: false,
type: `boolean`,
description: `Removes all default Tamagui styles.`,
},
]}
/>
### Checkbox.Indicator
`Checkbox.Indicator` extend ThemeableStack inheriting all the
[props](/docs/components/stacks#themeablestack), plus:
---
# Source: https://tamagui.dev/docs/intro/compiler-install.md
---
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 a more
[in-depth background](/docs/intro/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
statically analyze them and optimize them down to platform-native primitives.
The end result is less abstraction - like a `div` on web, or 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`.
## Install
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',
// optional advanced optimization of styled() definitions within your app itself, not just ones in your components option
// default is false
enableDynamicEvaluation: false,
}
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:
```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.
Add `@tamagui/vite-plugin` and update your `vite.config.ts`:
```tsx
import { tamaguiPlugin } from '@tamagui/vite-plugin'
export default defineConfig({
plugins: [
tamaguiPlugin({
// points to your tamagui config file
config: 'src/tamagui.config.ts',
// points to any linked packages or node_modules
// that have tamagui components to optimize
components: ['tamagui'],
// turns on the optimizing compiler
optimize: true,
}),
],
})
```
### Next.js
See the ([guide](/docs/guides/next-js)) for more complete setup.
Add `@tamagui/next-plugin` and configure your `next.config.js`. Here we show a
fuller scope of the options
```js
// 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:
// 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:
// 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](/docs/guides/next-js) 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.
```bash
yarn add @tamagui/babel-plugin
```
Add to your `babel.config.js`:
```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 ``.
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](/docs/guides/expo) for more information on setting up
Expo. It's as simple as adding the babel plugin.
### 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.
#### Setup
1. Install:
```bash
yarn add -D @tamagui/cli
```
2. Create a `tamagui.build.ts` config file in your project root:
```ts
import type { TamaguiBuildOptions } from 'tamagui'
export default {
config: './tamagui.config.ts',
components: ['tamagui'],
importsWhitelist: ['constants.js', 'colors.js'],
outputCSS: './public/tamagui.css',
} satisfies TamaguiBuildOptions
```
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
# 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` or `.ios.tsx` extensions are optimized for web only
- Files with `.native.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** - just 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"
}
}
```
The Tamagui CLI optimizes your components in-place, then your bundler processes
the already-optimized files.
## Props
All compiler plugins accept the same options:
### 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:
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.
### 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 { View } from '@tamagui/core'
export default () =>
```
### 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.
---
# Source: https://tamagui.dev/docs/core/config-v4.md
---
title: Quick Config
description: Easy config and themes with @tamagui/config/v4
---
Config v4 simplifies configuration, generates much more useful themes, and
adds a new helper function for generating your own theme suite called
`createThemes`.
First you'll want to add it:
```bash
npm install @tamagui/config
```
Using the new `defaultConfig` is the easiest way to start:
```tsx fileName="tamagui.config.ts"
import { defaultConfig } from '@tamagui/config/v4'
import { createTamagui } from 'tamagui'
export const config = createTamagui(defaultConfig)
type CustomConfig = typeof config
// ensure types work
declare module 'tamagui' {
interface TamaguiCustomConfig extends CustomConfig {}
}
```
It gives you a robust suite of themes, a few shorthands that mostly align with
Tailwind, and default settings that prevent bugs.
If you like it but want to make some changes, you can
[copy and paste the source code](https://github.com/tamagui/tamagui/blob/5b0c075e92688ac85737fe97c98cd77322f034b0/code/core/config/src/v4.ts)
into your app and customize to your taste.
If you want to learn to create your own custom theme suite rather than use the
included defaults, check out the [Creating Custom Themes
Guide](/docs/guides/theme-builder).
We recommend adding `settings.styleCompat` to the default config, setting it to
be "react-native". This will align the default flexBasis of `flex` to be `0`,
not `auto`, matching the default in React Native. This setting will become the
default in the upcoming Tamagui v2 release. We left it out of Config v4 because
we wanted an easier transition for existing users, but if you are a new user, we
recommend this setting.
---
### Default Configuration
You can take or leave as much of the default configuration as you please. Each
part is exported separately, or all together as `defaultConfig`.
#### Themes
Config v4 has a new helper function `createThemes`, an opinionated way to
generate a suite of themes. It also comes with default themes that include
minimal grayscale light and dark themes, and an inverted "accent" theme.
It's easiest to get a feel for both createThemes the default themes by reading
the source for how `defaultThemes` are defined:
```tsx fileName=themes.ts
import * as Colors from '@tamagui/colors'
import { createThemes, defaultComponentThemes } from '@tamagui/config/v4'
const darkPalette = [
'#050505',
'#151515',
'#191919',
'#232323',
'#282828',
'#323232',
'#424242',
'#494949',
'#545454',
'#626262',
'#a5a5a5',
'#fff',
]
const lightPalette = [
'#fff',
'#f8f8f8',
'hsl(0, 0%, 96.3%)',
'hsl(0, 0%, 94.1%)',
'hsl(0, 0%, 92.0%)',
'hsl(0, 0%, 90.0%)',
'hsl(0, 0%, 88.5%)',
'hsl(0, 0%, 81.0%)',
'hsl(0, 0%, 56.1%)',
'hsl(0, 0%, 50.3%)',
'hsl(0, 0%, 42.5%)',
'hsl(0, 0%, 9.0%)',
]
const lightShadows = {
shadow1: 'rgba(0,0,0,0.04)',
shadow2: 'rgba(0,0,0,0.08)',
shadow3: 'rgba(0,0,0,0.16)',
shadow4: 'rgba(0,0,0,0.24)',
shadow5: 'rgba(0,0,0,0.32)',
shadow6: 'rgba(0,0,0,0.4)',
}
const darkShadows = {
shadow1: 'rgba(0,0,0,0.2)',
shadow2: 'rgba(0,0,0,0.3)',
shadow3: 'rgba(0,0,0,0.4)',
shadow4: 'rgba(0,0,0,0.5)',
shadow5: 'rgba(0,0,0,0.6)',
shadow6: 'rgba(0,0,0,0.7)',
}
const extraColors = {
black1: darkPalette[0],
black2: darkPalette[1],
black3: darkPalette[2],
black4: darkPalette[3],
black5: darkPalette[4],
black6: darkPalette[5],
black7: darkPalette[6],
black8: darkPalette[7],
black9: darkPalette[8],
black10: darkPalette[9],
black11: darkPalette[10],
black12: darkPalette[11],
white1: lightPalette[0],
white2: lightPalette[1],
white3: lightPalette[2],
white4: lightPalette[3],
white5: lightPalette[4],
white6: lightPalette[5],
white7: lightPalette[6],
white8: lightPalette[7],
white9: lightPalette[8],
white10: lightPalette[9],
white11: lightPalette[10],
white12: lightPalette[11],
}
const generatedThemes = createThemes({
componentThemes: defaultComponentThemes,
base: {
palette: {
dark: darkPalette,
light: lightPalette,
},
// for values we don't want being inherited onto sub-themes
extra: {
light: {
...Colors.blue,
...Colors.green,
...Colors.red,
...Colors.yellow,
...lightShadows,
...extraColors,
shadowColor: lightShadows.shadow1,
},
dark: {
...Colors.blueDark,
...Colors.greenDark,
...Colors.redDark,
...Colors.yellowDark,
...darkShadows,
...extraColors,
shadowColor: darkShadows.shadow1,
},
},
},
// inverse accent theme
accent: {
palette: {
dark: lightPalette,
light: darkPalette,
},
},
childrenThemes: {
blue: {
palette: {
dark: Object.values(Colors.blueDark),
light: Object.values(Colors.blue),
},
},
red: {
palette: {
dark: Object.values(Colors.redDark),
light: Object.values(Colors.red),
},
},
yellow: {
palette: {
dark: Object.values(Colors.yellowDark),
light: Object.values(Colors.yellow),
},
},
green: {
palette: {
dark: Object.values(Colors.greenDark),
light: Object.values(Colors.green),
},
},
},
})
export type TamaguiThemes = typeof generatedThemes
/**
* This is an optional production optimization: themes JS can get to 20Kb or more.
* Tamagui has ~1Kb of logic to hydrate themes from CSS, so you can remove the JS.
* So long as you server render your Tamagui CSS, this will save you bundle size:
*/
export const themes: TamaguiThemes =
// we define this in our bundler plugins, but you can also use your framework/bundlers
// built in environment variables to ensure this is shaken away on client js bundles
process.env.TAMAGUI_ENVIRONMENT === 'client' &&
process.env.NODE_ENV === 'production'
? {}
: (generatedThemes as any)
```
So what have we done?
- `base` will always generate `light` and `dark` as your root themes. We plan to
add support for generating just one base theme, but you can manually remove
either light or dark if you please.
- Our `base` themes are grayscale, defined by their palettes.
- The `accent` option is an opinionated way to have a "contrasting theme". When
set, it generates a few things:
- `light_accent` and `dark_accent` themes
- `$accent1` through `$accent12` on the base themes
- `childrenThemes` allows us to set up some sub-themes easily, this is
especially useful for when you want to make an area look like an error,
success, or warning state. That's why we default to setting `green`, `yellow`,
and `red`. Of course, you can swap these out for whatever you like.
- We also are setting `componentThemes` to `defaultComponentThemes`, which will
generate for us default styling for the Tamagui UI components. If you prefer
to not have default component themes, you can leave this out.
- You can use the `getTheme` callback to customize any generated theme. See the
[theme-builder guide](/docs/guides/theme-builder#customizing-themes-with-gettheme)
for details.
#### Settings
#### Media
The new media queries in v4 are also more simplified and aligned to Tailwind:
On the server, only xs and 2xs default to `true` to align generally give you a
[mobile-first design](https://en.wikipedia.org/wiki/Responsive_web_design)
setup, but of course you can change that. This only really affects `useMedia()`,
as Tamagui generates proper SSR-safe CSS for media queries on the server for any
style properties.
#### Shorthands
Config v4 has fewer shorthands, and aligns shorthands to Tailwind for
familiarity:
#### Tokens
Tokens default tokens are defined as:
```tsx
export const size = {
$0: 0,
'$0.25': 2,
'$0.5': 4,
'$0.75': 8,
$1: 20,
'$1.5': 24,
$2: 28,
'$2.5': 32,
$3: 36,
'$3.5': 40,
$4: 44,
$true: 44,
'$4.5': 48,
$5: 52,
$6: 64,
$7: 74,
$8: 84,
$9: 94,
$10: 104,
$11: 124,
$12: 144,
$13: 164,
$14: 184,
$15: 204,
$16: 224,
$17: 224,
$18: 244,
$19: 264,
$20: 284,
}
export const space = {
// ... space is the same as size, but also defines "negative" values for every size, like:
'$-1': -20,
}
export const zIndex = {
0: 0,
1: 100,
2: 200,
3: 300,
4: 400,
5: 500,
}
export const radius = {
0: 0,
1: 3,
2: 5,
3: 7,
4: 9,
true: 9,
5: 10,
6: 16,
7: 19,
8: 22,
9: 26,
10: 34,
11: 42,
12: 50,
}
```
Note that `color` tokens aren't defined, instead we are just using themes.
Also note that the "true" value on tokens is the "default" - Tamagui UI for now
uses defaults to determine how to size various things out of the box. This isn't
mandatory, and in version 2 of Tamagui we are working on a more clear solution.
#### Fonts
Only a `body` and `heading` font are exported, which both use "system" font
families:
You can create your own system font using the exported `createSystemFont`
helper, or of course swap out your own fonts altogether.
---
# Source: https://tamagui.dev/ui/dialog.md
---
title: Dialog
description: Show a modal with configurable layout and accessible actions.
name: dialog
component: Dialog
package: dialog
demoName: Dialog
---
```tsx hero template=Dialog
```
Dialog is a great way to show content inside a new floating window above
content. Be sure to open the code example above for a copy-paste implementation.
## Installation
Dialog is already installed in `tamagui`, or you can install it independently:
```bash
npm install @tamagui/dialog
```
If you aren't using `tamagui` and instead using the `@tamagui/dialog` package
separately, you'll first need to install the `@tamagui/portal` package:
```bash
npm install @tamagui/portal
```
Then add `PortalProvider` to the root of your app:
```tsx fileName="App.tsx"
import { PortalProvider } from '@tamagui/portal'
import YourApp from './components/YourApp'
function App() {
return (
)
}
export default App
```
## Anatomy
```tsx
import { Dialog } from 'tamagui' // or '@tamagui/dialog'
export default () => (
)
```
## Scoping
Dialog supports scoping which lets you mount one or more Dialog instances at the
root of your app, while having a deeply nested child Trigger or Content attach
to the proper parent Dialog instance.
In performance sensitive areas you may want to take advantage of this, it allows
you to only need to render the Dialog.Trigger inside the sensitive area as
Dialogs aren't the cheapest component - they have a lot of functionality.
Here's the basic anatomy of using `scope` and placing your Dialog higher up for
performance:
```tsx fileName=_layout.tsx
import { Dialog } from 'tamagui'
// in your root layout:
export default ({ children }) => (
)
```
```tsx fileName=UserProfile.tsx
export default () => (
Open Profile
)
```
Note that the `Trigger` scope ties to the `Dialog` scope.
## Dismissal Behavior
By default, dialogs can be dismissed by:
- Clicking outside the dialog content (on the overlay)
- Pressing the Escape key
- Clicking a Dialog.Close element
### Modal vs Non-Modal Dialogs
- **Modal dialogs** (`modal={true}`, which is the default):
- In v1, have `disableOutsidePointerEvents` set to `true` by default
- Still dismiss on outside click, but prevent interaction with elements behind the dialog
- Prevent right-click dismissal (right-clicks on the overlay are ignored)
- **Non-modal dialogs** (`modal={false}`):
- Allow interaction with elements behind the dialog
- Dismiss on any outside click
- Do not trap focus
### Preventing Outside Dismissal
To prevent a dialog from closing when clicking outside:
```tsx
{
event.preventDefault()
}}
>
{/* Dialog contents */}
```
## API Reference
### Dialog
Contains every component for the dialog. Beyond
[Tamagui Props](/docs/intro/props), adds:
void',
},
{
name: 'modal',
type: 'boolean',
default: 'true',
description: `Renders into root of app instead of inline`,
},
{
name: 'disableRemoveScroll',
type: 'boolean',
required: false,
description: `Used to disable the automatic removal of scrolling from the page when open.`,
},
]}
/>
### Dialog.Trigger
Just [Tamagui Props](/docs/intro/props).
### Dialog.Portal
Renders Dialog into appropriate container. Beyond
[Tamagui Props](/docs/intro/props), adds:
### Dialog.Content
Main container for Dialog content, this is where you should apply animations.
Beyond [Tamagui Props](/docs/intro/props), adds:
### Dialog.Overlay
Displays behind Content. Beyond [Tamagui Props](/docs/intro/props), adds:
### Dialog.Title
Required. Can wrap in VisuallyHidden to hide.
Defaults to H2, see [Headings](/docs/components/headings).
### Dialog.Description
Required. Can wrap in VisuallyHidden to hide.
Defaults to Paragraph, see [Paragraph](/docs/components/text).
### Dialog.Close
Closes the Dialog, accepts the same props as YStack. Recommended to use with
your own component and `asChild`.
Just [Tamagui Props](/docs/intro/props).
### Dialog.FocusScope
Provides access to the underlying FocusScope component used by Dialog for focus
management. Can be used to control focus behavior from a parent component.
void',
description: `Event handler called when auto-focusing on mount. Can be prevented`,
},
{
name: 'onUnmountAutoFocus',
type: '(event: Event) => void',
description: `Event handler called when auto-focusing on unmount. Can be prevented`,
},
]}
/>
### Dialog.Sheet
When used with `Adapt`, Dialog will render as a sheet when that breakpoint is
active.
See [Sheet](/docs/components/sheet) for more props.
Must use `Adapt.Contents` inside the `Dialog.Sheet.Frame` to insert the contents
given to `Dialog.Content`
```tsx
import { Dialog } from 'tamagui' // or '@tamagui/dialog'
export default () => (
)
```
Note that Dialog.Sheet currently doesn't preserve state of the contents when
it transitions between Sheet and Portal. In the future, we can do this on the
web using react-reparenting.
### PortalProvider
## Examples
### Inside native modals
If you're using native modals (maybe from react-navigation), you'll notice the
Dialogs won't show up inside the modal. To get around this, you should wrap your
screen inside `PortalProvider`, like so:
```tsx
import { PortalProvider } from 'tamagui'
// this component used in react-navigation/expo-router with `presentation: "modal"`
export function Page() {
return (
{/* rest of your page, including the Dialog... */}
)
}
```
---
# Source: https://tamagui.dev/docs/core/exports.md
---
title: 'Other exports'
description: Helpful functions and constants
---
Let's take a quick look through some of the useful other exports in
`@tamagui/core`.
## Constants
[Constants are re-exported from @tamagui/constants](https://github.com/tamagui/tamagui/blob/main/code/core/constants/src/constants.ts):
- `isWeb` - True if targeting the web. Will be true both for SSR and client.
- `isWindowDefined` - `typeof window !== 'undefined'`
- `isServer` - `isWeb && !isWindowDefined`.
- `isClient` - `isWeb && isWindowDefined`.
- `useIsomorphicLayoutEffect` - `isServer ? useEffect : useLayoutEffect`. Helper
for SSR rendering without warnings.
- `isChrome` - client-side Chrome
- `isWebTouchable` - web-only touch-device (client side only)
- `isTouchable` - True for any touch device (client side only).
---
## Helpers
### getConfig
The exported `getConfig` will return your fully parsed Tamagui config. This is
safer than importing it directly as it avoids circular imports. In general you
want to use `useTheme` to access themes, and
`getTokens`/`getToken`/`getTokenValue` for tokens, but for other parts of the
config this can be useful.
### insertFont
```tsx
type insertFont = (name: string, fontIn: GenericFont) => ParsedFont
```
Will add a new font after initial createTamagui call. Where `GenericFont` is the
same as a font passed to createTamagui, and `ParsedFont` is the font with the
subkeys prefixed with `$` and turned into variable objects.
### updateFont
The same as insertFont, but will update an existing font.
### isTamaguiComponent
```tsx
type isTamaguiComponent (component: any; name?: string) => boolean
```
If no name given, true if a Tamagui component, if name given ensures it's the
specific named Tamagui component.
### isTamaguiElement
```tsx
type isTamaguiElement (child: any; name?: string) => boolean
```
If no name given, true if a Tamagui ReactElement, if name given ensures it's the
specific named Tamagui component element.
### getTokens
```tsx
;() => TokensParsed
```
Returns the parsed Tamagui config object of all your tokens, can be used at
runtime to get values from tokens. If you are looking to get a single token
value, prefer `getToken` or `getTokenValue`.
### getToken
```tsx
;(name: Token, group?: keyof Tokens) => any
```
Given the specific name of a token or a name + group, will return the value as
either a variable on the web, or raw value on native. So if you define a size
token with key `small` and value of `14`:
```tsx
getToken('$size.small') // returns on web var(--size-small), native 14
getToken('$small', 'size') // returns on web var(--size-small), native 14
```
### getTokenValue
```tsx
;(name: Token, group?: keyof Tokens) => any
```
Similar to getToken, but always returns the raw value rather than the variable
name. If you define a size token with key `small` and value of `14`:
```tsx
getTokenValue('$size.small') // returns 14
getTokenValue('$small', 'size') // returns 14
```
### getExpandedShorthands
```tsx
;(props: Object) => Object
```
Take props, returns new object with all shorthand props expanded.
### themeable
```tsx
themeable(component: Comp): Comp
```
A higher order component that accepts `theme` and `themeInverse`, rendering them
onto `Theme` before rendering your component.
### getVariable
```tsx
type getVariable = (value: Variable) => string
```
If given a `Variable` - which comes from a parsed theme or token returned from
`createTamagui`, `useTheme` or `getTokens` (read more
[on the useTheme docs](/docs/core/use-theme)).
Calling `getVariable(useTheme().color)` returns `var(--color)` on the web, and
`#fff` on other platforms.
### setOnLayoutStrategy
This is a low-level utility to control `onLayout` performance. By default,
Tamagui `onLayout` on the web uses the `"async"` strategy (see below), which
gives a good balance of accuracy and performance. For extremely accurate to
native `onLayout`, you can use `"sync"`, but it can cause performance issues in
typical usage.
Call `setOnLayoutStrategy` with a single string argument:
- `"off"` will pause all `onLayout` callbacks from running.
- `"sync"` will run every frame and update every frame if necessary.
- `"async"` will use an async getBoundingClientRect strategy that avoids
repaints/reflows, and also waits 2 frames of debouncing before triggering the
callback.
The `async` stretegy generally has great performance on web, and it will also
ensure to call `onLayout` sync on mount the first time, so you avoid jitters.
---
## Hooks
### useProps
Pass in props that include media styles and shorthands, get back a flat object
of props with the current media styles merged and shorthands expanded. This is
an advanced pattern that should be used sparingly, as it will trigger updates on
every media query used in the props object. Helpful for more complex components
that need to pass down props they are given to children (a common example being
the `size` prop, if it's controlling children that also accept size).
```tsx
// if props is:
// { size: '$2', $sm: { size: '$4' } }
// and $sm is active, activeProps will be:
// { size: '$4' }
// if shorthands like m => margin, then m always expands to margin
function MyButton(props) {
const activeProps = useProps(props)
}
```
You can pass an option to disable the shorthands expansion, and another option
to resolve any token strings into their respective theme or token value:
```tsx
function MyButton(props) {
const activeProps = useProps(props)
}
```
useProps takes a second argument, which is the same as useStyle, documented in
the next section.
### useStyle
Pass in props that include media styles and shorthands, get back a flat style
object with the current media styles merged, shorthands expanded, and all
theme/token values resolved (into CSS variables on the web or values on native
by default). This will remove any non-style values and will by default expand
all variants and resolve all values.
```tsx
// if props is:
// { color: '$background', $sm: { color: '$color' } }
// and $sm is active, activeProps will be on web:
// { color: 'var(--color)' }
// and on native will resolve to the actual theme or token value:
// { color: '#fff' }
// this resolve behavior can be controlled, see the docs just below.
// if shorthands like m => margin, then m expands to margin
function MyButton(props) {
const style = useStyle(props)
}
```
It can take a couple options to control how it resolves values and handles
shorthands:
```tsx
function MyButton(props) {
const style = useStyle(props, {
// option to disable the shorthand expansion
disableExpandShorthands: true,
// option to change how values are resolved
// 'auto' (default) resolves to optimized value on all platforms (CSS variable on web, DynamicColor on iOS, not optimized on android)
// 'value' resolves to the raw theme/token string on all platforms (the same as useTheme()[name].val)
resolveValues: 'value',
// pass a Tamagui component to resolve any custom variants:
forComponent: CustomComponent,
})
}
```
### usePropsAndStyle
Similar to the above two hooks, this returns your fully resolved/merged/media
query flattened styles, but instead of returning _just_ props or _just_ style,
it returns both of them split out, as well as returning the result of useTheme()
and useMedia() that it uses internally:
```tsx
function MyButton(props) {
const [props, style, theme, media] = usePropsAndStyle(props, opts)
// see useStyle to for documentation on opts
// we return theme, media as they are used internally already in the hook
}
```
### useThemeName
Returns the string name of the current theme.
### useIsTouchDevice
SSR-friendly, only true on if native touchable or web touchable device (client
side, not server side).
### useDidFinishSSR
SSR-friendly, returns true if SSR has completed on the client, false before
hydration done. On server it's always false.
### ClientOnly
A component that opts its children out of SSR. When wrapped in `ClientOnly`,
Tamagui avoids double-renders in various places which results in faster
client-side rendering.
```tsx
import { ClientOnly } from 'tamagui' // or '@tamagui/core'
// Children will render only on the client, skipping SSR entirely
// Can be conditionally enabled
```
---
## Type Helpers
### GetProps
Fetches the type of props for a Tamagui component:
```tsx
import { Stack, GetProps, styled } from '@tamagui/core'
const X = styled(Stack, {})
type XProps = GetProps
```
### GetRef
Fetches the type of a ref for a Tamagui component, or any React component:
```tsx
import { Stack, GetRef, styled } from '@tamagui/core'
const X = styled(Stack, {})
const MyComponent = () => {
const ref = useRef>()
return
}
```
---
# Source: https://tamagui.dev/docs/core/font-language.md
---
title: FontLanguage
description: Using custom fonts for each language
---
The `FontLanguage` component works with the Tamagui design system, allowing you to define custom fonts per-language.
### Rationale
Before `FontLanguage`, you could get custom fonts with some effort. Simply create a `body` font, and a `body_french` font, and switch it out like so on relevant text components: ``.
This would work, but having to update every Text component on page with a conditional would be terribly verbose and slow.
With `FontLanguage`, you can do:
```tsx
Hello world
```
Even better, with `styled`:
```tsx
const P = styled(Text, { fontFamily: '$body' })
Hello world
```
On the web, FontLanguage uses CSS Variables to avoid re-renders even when changing languages.
### Usage
In your `tamagui.config.ts`, you can add a language variant for any font by adding a suffix with a custom name for that language. Here's a stripped down config showing how you can add the custom `_cn` suffix for a Chinese language font:
```tsx
import { createFont, createTamagui, createTokens } from 'tamagui'
export default createTamagui({
fonts: {
body: createFont({
family: 'Inter, Helvetica, Arial, sans-serif',
// ...
}),
body_cn: createFont({
family: 'Inter-CN',
// ...
}),
},
})
```
You'll then need to load the `Inter` and `Inter-CN` fonts for the platforms you support. Once you do, you'll get fully typed support for changing any `body` font to `cn` in a component:
```tsx
import { FontLanguage, Text } from 'tamagui' // or '@tamagui/core'
export default (
你好
)
```
The name you choose for the suffix is up to you, and Tamagui will treat any font with a `_` separator as a language variant.
To reset your font back to your base `body` font, you can use the reserved key `default`:
```tsx
import { FontLanguage, Text } from 'tamagui' // or '@tamagui/core'
export default (
你好
Hello
)
```
---
# Source: https://tamagui.dev/ui/form.md
---
title: Form
description: A simple form component for native and web.
name: form
component: Form
package: form
demoName: Form
---
```tsx hero template=Forms
```
## Installation
Form is already installed in `tamagui`, or you can install it independently:
```bash
npm install @tamagui/form
```
## Anatomy
```tsx
import { Form } from 'tamagui' // or '@tamagui/form'
export default () => (
)
```
## API Reference
### Form
void',
required: true,
description: `Must use Form.Trigger to ensure onSubmit will callback.`,
},
]}
/>
### Form.Trigger
Wrap this around your submitting element to make the form submit. We recommend using `asChild` to a child element of your choosing for more control.
Accepts [Tamagui Props](/docs/intro/props).
---
# Source: https://tamagui.dev/ui/group.md
---
title: Group
description: Render horizontal or vertical groups easily.
name: group
component: Group
demoName: Group
---
# Group
Layout buttons and more with groups.
```tsx hero template=Group
```
## Usage
You can use `Group` by itself with the `axis` property determining the direction it assumes.
By default, Groups will control the border radius of their children automatically - the first and last children will get their start/end radius set to match group radius. If it's a `YGroup`, it will adjust top/bottom radius. `XGroup` adjusts the left/right radius.
You can use Groups with or without `Group.Item`, depending on if you want to spacing and separators to be handled based on direct children, or on each Item rendered.
```tsx
import { Button, XGroup } from 'tamagui'
// usage with Item:
export default () => (
FirstSecondThird
)
```
For a simpler use case and backwards compat, you can also use it without `Group.Item`, which will just apply borders and spacing based on direct children. Note that Group will detect if any `Group.Item` is inside it, and automatically switch modes. If no Item, it spaces direct children:
```tsx
import { Button, XGroup } from 'tamagui'
// usage without Item:
export default () => (
FirstSecondThird
)
```
## Sizing
The `size` property will use your tokens to grab the appropriate radius for borderRadius values which it will pass to the first and last child as style props for borderRadius.
```tsx
import { Button, XGroup } from 'tamagui'
export default () => (
FirstSecondThird
)
```
## Disabled
The `disabled` property will pass to children
## Group API
### Group
`Group`, `XGroup`and`YGroup` extend [YStack](/docs/components/stacks), getting [Tamagui standard props](/docs/intro/props), plus:
### Group.Item
Wrap each of `XGroup` or `YGroup`'s children in one of these. It lets Tamagui apply the needed styles to them.
Accepts only a `children` prop.
---
# Source: https://tamagui.dev/ui/headings.md
---
title: Headings
description: Heading components that mimic HTML equivalents
name: html
component: Headings
package: text
demoName: Headings
---
```tsx hero template=Headings
```
## Installation
Headings is already installed in `tamagui`, or you can install it independently:
```bash
npm install @tamagui/text
```
```tsx
import { H1, H2, H3, H4, H5, H6, Heading } from 'tamagui'
export default () => (
<>
Heading 1
Heading 2
Heading 3
Heading 4
Heading 5
Heading 6
Heading
>
)
```
The headings all extend from the base `Heading` component. Note, this is just our own theme for Inter headings, but you can change the styles for any font completely.
Tamagui expects for your [font.size](/docs/core/configuration#font-tokens) to have the keys 1-10 so headings work with your font tokens automatically.
## How it works
The `Heading` component is defined as follows:
```tsx
export const Heading = styled(Paragraph, {
tag: 'span',
name: 'Heading',
accessibilityRole: 'header',
fontFamily: '$heading',
size: '$8',
margin: 0,
})
```
Note that Heading, and H1-H6 all default to the `heading` font family that must be defined in your tamagui.config.ts.
Because [Paragraph](/docs/components/text#paragraph) extends [SizableText](/docs/components/text#sizabletext), you get automatic styles based on your font theme. Let's see how `SizableText` defines the size variant, roughly, which gives a good idea of how Tamagui works, and how you could create or change your own headings at a lower level.
```tsx
import { Text } from 'tamagui' // or '@tamagui/core'
const SizableText = styled(Text, {
name: 'SizableText',
fontFamily: '$body',
color: '$color',
variants: {
size: {
'...fontSize': (val, { font, props }) => {
const fontSize = font.size[val]
const lineHeight = font.lineHeight[val]
const fontWeight = font.weight[val]
const letterSpacing = font.letterSpacing[val]
const fontStyle = font.style?.[val]
const textTransform = font.transform?.[val]
return {
fontStyle,
textTransform,
fontWeight,
letterSpacing,
fontSize,
lineHeight,
}
},
},
},
defaultVariants: {
// note tamagui uses a generic "true" token that your sizes should set to be the same as the default on your scale
size: '$true',
},
})
```
## API Reference
### Heading
Headings extend [SizableText props](/docs/components/text#sizabletext) inheriting all the [Tamagui standard props](/docs/intro/props).
---
# Source: https://tamagui.dev/ui/html-elements.md
---
title: HTML Elements
description: Render semantic HTML with these elements.
name: html-elements
component: Layouts
---
To assist in creating accessible web apps, the following components are exported, all mapping directly to DOM elements of the lowercase names:
- `Section` (`section`)
- `Article` (`article`)
- `Main` (`main`)
- `Header` (`header`)
- `Aside` (`aside`)
- `Footer` (`footer`)
- `Nav` (`nav`)
## Installation
It's exported by `tamagui`, or:
```bash
npm install @tamagui/elements
```
## HTML element props
All HTML components extend View, inheriting all the [Tamagui standard props](/docs/intro/props).
---
# Source: https://tamagui.dev/ui/image.md
---
title: Image
description: React Native Web Image + Tamagui style props.
name: html
component: Image
package: image
demoName: Image
---
```tsx hero template=Image
```
## Installation
Image is already installed in `tamagui`, or you can install it independently:
```bash
npm install @tamagui/image
```
## Usage
Note that you need to set `source` like so - the `width` and `height` properties apply as styles around the image, but the actual image needs `source.width` and `source.height` for React Native:
```tsx
export default () => (
)
```
## API Reference
### Image
[Tamagui props](/docs/intro/props) + [React Native Web Image props](https://necolas.github.io/react-native-web/docs/image/).
---
# Source: https://tamagui.dev/ui/inputs.md
---
title: Input & Textarea
name: inputs
description: Flexible form fields in styled and unstyled forms.
component: Input
demoName: Inputs
---
```tsx hero template=Inputs
```
Using the same base component TextInput, from [React Native](https://reactnative.dev/docs/textinput) or [React Native Web](https://necolas.github.io/react-native-web/docs/text-input/), Tamagui simply wraps these components to allow the full set of style props, as well as scaling all the styles up or down using the `size` property, much like Button.
## Installation
Input 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
)
```
---
# Source: https://tamagui.dev/ui/label.md
---
title: Label
description: Label form elements with accessibility.
name: label
component: Label
package: label
demoName: Label
---
```tsx hero template=Label
```
## Installation
Label is already installed in `tamagui`, or you can install it independently:
```bash
npm install @tamagui/label
```
## Usage
```tsx
import { Label } from 'tamagui'
export default () => (
<>
>
)
```
## Accessibility
Use with Input or other form elements to automatically get correct labelling by id and aria-labelledby. You can also use the provided `useLabelContext` hook to build your own controls.
## API Reference
### Label
Labels extend Stack views inheriting all the [Tamagui standard props](/docs/intro/props), plus:
---
# Source: https://tamagui.dev/ui/linear-gradient.md
---
title: Linear Gradient
description: Linear Gradient that works with Tamagui style props.
name: html
component: LinearGradient
demoName: LinearGradient
---
# Linear Gradient
Linear Gradient that works with Tamagui style props.In beta, currently doesn't resolve theme values for the colors property.
```tsx hero template=LinearGradient
```
### Usage
We've included `` so it can accept Tamagui style props, and to patch a bug with the current expo version with some Webpack configurations.
---
# Source: https://tamagui.dev/ui/list-item.md
---
title: ListItem
description: A component for showing columns of items.
name: list-item
component: ListItem
package: list-item
demoName: ListItem
---
```tsx hero template=ListItem
```
## Installation
ListItem is already installed in `tamagui`, or you can install it independently:
```bash
npm install @tamagui/list-item
```
## Usage
```tsx
import { ListItem } from 'tamagui'
export default () => Lorem ipsum
```
## Sizing
Sizing listItems provides a unique challenge especially for a compiler, because you need to adjust many different properties - not just on the outer frame, but on the text wrapped inside. Tamagui supports adjusting the padding, border radius, font size and icons sizes all in one with the `size` prop.
```tsx
import { ListItem } from 'tamagui'
export default () => Lorem ipsum
```
Given your theme defines a size `6`, the listItem will adjust all of the properties appropriately. You can also pass a plain number to get an arbitrary size.
## Icon Theming
You can pass icons as either elements or components. If passing components, Tamagui will automatically pass the `size` and `color` prop to them based on your theme.
## Customization
ListItem only supports a limited subset of text props directly, and doesn't accept `hoverStyle` text props. If you need more control, you can do a simple customization:
```tsx
import { forwardRef } from 'react'
import {
ListItemFrame,
ListItemText,
ListItemTitle,
ListItemSubtitle,
styled,
themeable,
useListItem,
} from 'tamagui'
const CustomListItemFrame = styled(ListItemFrame, {
backgroundColor: 'orange', // or "$color", etc.
})
const CustomListItemTitle = styled(ListItemTitle, {
color: 'blue',
})
const CustomListItemSubtitle = styled(ListItemSubtitle, {
color: 'pink',
})
const CustomListItemText = styled(ListItemText, {
color: 'red',
})
export const ListItem = CustomListItemFrame.styleable((propsIn, ref) => {
const { props } = useListItem(propsIn, {
Title: CustomListItemTitle,
Text: CustomListItemText,
Subtitle: CustomListItemSubtitle,
})
return
})
```
There are 3 different components you can customize: `ListItemText`, `ListItemSubtitle` and `ListItemTitle`.
You can include whichever one you want to customize specifically.
If you only want to customize the the text pieces, you don't have to include `CustomListItemFrame`:
```tsx
// all the text changes from above, with a default ListItemFrame
export const ListItem = themeable(
forwardRef((propsIn, ref) => {
const { props } = useListItem(propsIn, {
Title: CustomListItemTitle,
Text: CustomListItemText,
Subtitle: CustomListItemSubtitle,
})
return
})
)
```
## API Reference
### ListItem
ListItems extend Stack views inheriting all the [Tamagui standard props](/docs/intro/props), plus:
### ListItem.Title
`ListItem.Title` extend `SizableText` inheriting all the [props](/docs/components/text#sizabletext).
### ListItem.Subtitle
`ListItem.Subtitle` extend `SizableText` inheriting all the [props](/docs/components/text#sizabletext).
### ListItem.Text
`ListItem.Text` extend `SizableText` inheriting all the [props](/docs/components/text#sizabletext).
---
# Source: https://tamagui.dev/ui/lucide-icons.md
---
title: Lucide Icons
description: Cross-platform compatible SVG based icons
demoName: LucideIcons
---
## Installation
```sh
yarn add react-native-svg @tamagui/lucide-icons
```
## Usage
Use them as regular React components
```tsx
import { Button } from 'tamagui'
import { Plus } from '@tamagui/lucide-icons'
// Button will automatically pass size/theme to icon
export default () => (
Hello world
)
// or you can control it
export default () => (
}>
Hello world
)
```
They accept your tokens/theme keys for color and size.
## Credit
The great [Lucide Icons](https://lucide.dev/), a superset of the wonderful [Feather Icons](https://feathericons.com/).
---
# Source: https://tamagui.dev/ui/new-inputs.md
---
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
)
```
---
# Source: https://tamagui.dev/ui/popover.md
---
title: Popover
description: A simple popover component
name: popover
component: Popover
package: popover
demoName: Popover
---
# Popover
Show content with a trigger in a floating pane
```tsx hero template=Popover
```
Popovers are a great way to show content that's only visible when trigger is pressed, floating above the current content.
## Installation
Popover is already installed in `tamagui`, or you can install it independently:
```bash
npm install @tamagui/popover
```
### PortalProvider
When rendering into root of app instead of inline, you'll first need to install the `@tamagui/portal` package:
```bash
npm install @tamagui/portal
```
Then add `PortalProvider` to the root of your app:
```tsx fileName="App.tsx"
import { PortalProvider } from '@tamagui/portal'
import YourApp from './components/YourApp'
function App() {
return (
)
}
export default App
```
## Anatomy
```tsx
import { Popover, Adapt } from 'tamagui' // or '@tamagui/popover'
export default () => (
{/* ScrollView is optional, can just put any contents inside if not scrollable */}
{/* ... */}
{/* ... */}
{/* optionally change to sheet when small screen */}
{/* you can also use */}
)
```
## API Reference
### Popover
Contains every component for the popover.
void',
required: false,
},
{
name: 'modal',
type: 'boolean',
default: 'true',
required: false,
description: `Renders into root of app instead of inline.`,
},
{
name: 'keepChildrenMounted',
type: 'boolean',
required: false,
description: `By default, Popover removes children from DOM/rendering when fully hidden. Setting true will keep children mounted even when hidden. This can be beneficial for performance if your popover content is expensive to render.`,
},
{
name: 'stayInFrame',
type: 'ShiftProps | boolean',
required: false,
description: `Keeps the Popover inside the frame, see floating-ui shift().`,
},
{
name: 'allowFlip',
type: 'FlipProps | boolean',
required: false,
description: `Moves the Popover to other sides when space allows it, see floating-ui flip().`,
},
{
name: 'offset',
type: 'OffsetOptions',
required: false,
description: `Determines the distance the Popover appears from the target, see floating-ui offset().`,
},
{
name: 'hoverable',
type: 'boolean | UseFloatingProps',
required: false,
description: `Allows hovering on the trigger to open the popover. See UseFloatingProps from floating-ui: accepts boolean or object of { delay: number, restMs: number, handleClose: Function, mouseOnly: boolean, move: boolean }`,
},
]}
/>
If using `modal={true}` (which is `true` by default), refer to the [PortalProvider installation](/ui/popover/1.83.0#portalprovider) for more information.
### Popover.Arrow
Popover.Arrow can be used to show an arrow that points at the Trigger element. In order for the Arrow to show you must have a Trigger element within your Popover. Arrows extend YStack, see [Stacks](/docs/components/stacks).
### Popover.Trigger
Used to trigger opening of the popover when uncontrolled, just renders a YStack, see [Stacks](/docs/components/stacks).
### Popover.Content
Renders as SizableStack which is just a YStack (see [Stacks](/docs/components/stacks)) with an extra `size` prop that accepts any `SizeTokens`.
Used to display the content of the popover.
### Popover.Anchor
Renders as YStack, see [Stacks](/docs/components/stacks).
When you want the Trigger to be in another location from where the Popover attaches, use Anchor. When used, Anchor is where the Popover will attach, while Trigger will open it.
### Popover.Sheet
When used with `Adapt`, Popover will render as a sheet when that breakpoint is active.
See [Sheet](/docs/components/sheet) for more props.
Must use `Adapt.Contents` inside the `Popover.Sheet.Frame` to insert the contents given to `Popover.Content`
### Popover.ScrollView
Must be nested inside Content. Renders as a plain React Native ScrollView. If used alongside `` and Sheet, Tamagui will automatically know to remove this ScrollView when swapping into the Sheet, as the Sheet must use it's own ScrollView that handles special logic for interactions with dragging.
---
# Source: https://tamagui.dev/ui/portal.md
---
title: Portal
description: Send items to other areas of the tree.
name: portal
component: Portal
package: portal
---
Portal is included in `tamagui` as it's used by a few components. For now, it's simply using [@gorhom/portal](https://github.com/gorhom/react-native-portal).
---
# Source: https://tamagui.dev/ui/progress.md
---
title: Progress
description: Show percent completion with a progress bar.
name: progress
component: Progress
package: progress
demoName: Progress
---
```tsx hero template=Progress
```
Adheres to the{' '}
progressbar role requirements
,
]}
/>
## Installation
Progress is already installed in `tamagui`, or you can install it independently:
```bash
npm install @tamagui/progress
```
## Usage
The `value` property controls the percent, but you can override it by adjusting the `x` property.
```tsx
import { Button, Progress } from 'tamagui'
export default () => (
)
```
## API Reference
### Progress
Progress extends [ThemeableStack](/docs/components/stacks#themeablestack), getting [Tamagui standard props](/docs/intro/props), plus:
### Progress.Indicator
`Progress.Indicator` extends [ThemeableStack](/docs/components/stacks#themeablestack), getting [Tamagui standard props](/docs/intro/props).
---
# Source: https://tamagui.dev/ui/radio-group.md
---
title: RadioGroup
description: Use in a form to allow selecting one option from multiple.
name: radio-group
component: RadioGroup
package: radio-group
demoName: RadioGroup
---
```tsx hero template=RadioGroup
```
## Installation
RadioGroup is already installed in `tamagui`, or you can install it independently:
```bash
npm install @tamagui/radio-group
```
## Usage
```tsx
import { RadioGroup } from 'tamagui'
export default () => (
)
```
## API Reference
### RadioGroup
RadioGroup extend Stack views inheriting all the [Tamagui standard props](/docs/intro/props), plus:
void',
},
{
name: 'accentColor',
type: 'string',
description: 'Sets `accent-color` style when `native` prop is enabled',
},
]}
/>
### RadioGroup.Item
### RadioGroup.Indicator
RadioGroup.Indicator appears only when the parent Item is checked. it extends [ThemeableStack](/docs/components/stacks#themeablestack), getting [Tamagui standard props](/docs/intro/props) adding:
---
# Source: https://tamagui.dev/ui/scroll-view.md
---
title: ScrollView
description: React Native ScrollView with Tamagui props.
name: scrollView
component: ScrollView
package: scroll-view
demoName: ScrollView
---
```tsx hero template=ScrollView
```
## Installation
ScrollView is already installed in `tamagui`, or you can install it independently:
```bash
npm install @tamagui/scroll-view
```
## Usage
```tsx
import { ScrollView, YStack, ListItem } from 'tamagui'
export default () => (
1234
)
```
## API Reference
### ScrollView
See [React Native ScrollView](https://reactnative.dev/docs/scrollview) and [Tamagui style props](https://tamagui.dev/docs/intro/props).
---
# Source: https://tamagui.dev/ui/select.md
---
title: Select
description: Show a menu of items that users can select from.
name: select
component: Select
package: select
demoName: Select
---
```tsx hero template=Select
```
## Installation
Select is already installed in `tamagui`, or you can install it independently:
```bash
npm install @tamagui/select
```
## Anatomy
```tsx
import { Select } from 'tamagui' // or '@tamagui/select'
export default () => (
)
```
Note that Select only works on Native using the Adapt component to adapt it to
a Sheet. See below for docs.
## API Reference
### Select
Contains every component for the select:
void',
description: `Callback on value change`,
},
{
name: 'open',
type: 'boolean',
description: `Controlled open value`,
},
{
name: 'defaultOpen',
type: 'boolean',
description: `Default open value`,
},
{
name: 'onOpenChange',
type: '(open: boolean) => void',
description: `Callback on open change`,
},
{
name: 'dir',
type: 'Direction',
description: `Direction of text display`,
},
{
name: 'name',
type: 'string',
description: `For use in forms`,
},
{
name: 'native',
type: 'NativeValue',
description: `If passed, will render a native component instead of the custom one. Currently only \`web\` is supported.`,
},
]}
/>
### Select.Trigger
Extends [ListItem](/docs/components/list-item) to give sizing, icons, and more.
#### Select.Value
Extends [Paragraph](/docs/components/text), adding:
#### SelectContent
Main container for Select content, used to contain the up/down arrows, no API beyond children.
#### Select.ScrollUpButton
Inside Content first, displays when you can scroll up, stuck to the top.
Extends [YStack](/docs/components/stacks).
### Select.ScrollDownButton
Inside Content last, displays when you can scroll down, stuck to the bottom.
Extends [YStack](/docs/components/stacks).
### Select.Viewport
Extends [ThemeableStack](/docs/components/stacks#themeablestack). Contains scrollable content items as children.
Make sure to not pass `height` prop as that is managed internally because of
UX reasons and having a fixed height will break that behaviour
### Select.Group
Extends [YStack](/docs/components/stacks). Use only when grouping together items, alongside a Label as the first child.
### Select.Label
Extends [ListItem](/docs/components/list-item). Used to label Groups.
### Select.Item
Extends [ListItem](/docs/components/list-item). Used to add selectable values ot the list. Must provide an index as React Native doesn't give any escape hatch for us to configure that automatically.
### Select.ItemText
Extends [Paragraph](/docs/components/text). Used inside Item to provide unselectable text that will show above once selected in the parent Select.
### Select.Sheet
When used alongside ``, Select will render as a sheet when that breakpoint is active.
This is the only way to render a Select on Native for now, as mobile apps tend to show Select very differently from web and Tamagui wants to present the right abstractions for each platform.
See [Sheet](/docs/components/sheet) for more props.
Must use `Select.Adapt.Contents` inside the `Select.Sheet.Frame` to insert the contents given to `Select.Content`
```tsx
import { Select } from 'tamagui' // or '@tamagui/select'
export default () => (
)
```
---
# Source: https://tamagui.dev/ui/separator.md
---
title: Separator
description: Create borders between components.
name: separator
component: Separator
package: separator
demoName: Separator
---
```tsx hero template=Separator
```
## Installation
Separator is already installed in `tamagui`, or you can install it independently:
```bash
npm install @tamagui/separator
```
## Usage
Separator uses the `borderWidth` and `borderColor` attribute for its style, so be sure to override those when extending it.
```tsx
export default () => (
BlogDocsSource
)
```
## API Reference
### Separator props
Separators extend Stack views inheriting all the [Tamagui standard props](/docs/intro/props), plus:
---
# Source: https://tamagui.dev/ui/shapes.md
---
title: Shapes
description: Easy to use Square and Circle.
name: shapes
component: Square
package: shapes
demoName: Shapes
---
```tsx hero template=Shapes
```
## Installation
Shapes is already installed in `tamagui`, or you can install it independently:
```bash
npm install @tamagui/shapes
```
## Usage
Tamagui supports sizing shapes using your `size` tokens, or plain numbers.
```tsx
import { Circle, Square } from 'tamagui'
export default () => (
<>
>
)
```
## API Reference
### Square
`Square` extends Stack views inheriting all the [Tamagui standard props](/docs/intro/props), plus:
### Circle
`Circle` extends [Square](#square), setting `circular` to `true`.
---
# Source: https://tamagui.dev/ui/sheet.md
---
title: Sheet
description: A simple sheet component
name: sheet
component: Sheet
package: sheet
demoName: Sheet
---
# Sheet
A bottom sheet that slides up.
```tsx hero template=Sheet
```
### Anatomy
```tsx
import { Sheet } from 'tamagui' // or '@tamagui/sheet'
export default () => (
{/* ...inner contents */}
)
```
### API
#### <Sheet />
Contains every component for the sheet.
void',
description: `Called on change open, controlled or uncontrolled.`,
},
{
name: 'position',
type: 'number',
description: `Controlled position, set to an index of snapPoints.`,
},
{
name: 'defaultPosition',
type: 'number',
description: `Uncontrolled default position on mount.`,
},
{
name: 'snapPoints',
type: 'number[]',
default: `[80, 10]`,
description: `Array of numbers, 0-100 that corresponds to % of the screen it should take up. Should go from most visible to least visible in order. Use "open" prop for fully closed.`,
},
{
name: 'onPositionChange',
type: '(position: number) => void',
description: `Called on change position, controlled or uncontrolled.`,
},
{
name: 'dismissOnOverlayPress',
type: 'boolean',
default: 'true',
description: `Controls tapping on the overlay to close, defaults to true.`,
},
{
name: 'animationConfig',
type: 'Animated.SpringAnimationConfig',
default: 'true',
description: `Customize the spring used, passed to react-native Animated.spring().`,
},
{
name: 'disableDrag',
type: 'boolean',
description: `Disables all touch events to drag the sheet.`,
},
{
name: 'modal',
type: 'boolean',
description: `Renders sheet into the root of your app instead of inline.`,
},
{
name: 'dismissOnSnapToBottom',
type: 'boolean',
description: `Adds a snap point to the end of your snap points set to "0", that when snapped to will set open to false (uncontrolled) and call onOpenChange with false (controlled).`,
},
{
name: 'forceRemoveScrollEnabled',
type: 'boolean',
default: 'false',
description: `By default. Tamagui uses react-remove-scroll to prevent anything outside the sheet scrolling. This can cause some issues so you can override the behavior with this prop (either true or false).`,
},
{
name: 'portalProps',
type: 'Object',
description: `YStack props that can be passed to the Portal that sheet uses when in modal mode.`,
},
{
name: 'moveOnKeyboardChange',
type: 'boolean',
default: 'false',
description: 'Native-only flag that will make the sheet move up when the mobile keyboard opens so the focused input remains visible.'
}
]}
/>
#### <Overlay />
Displays behind Frame. Extends [YStack](/docs/components/stacks).
#### <Frame />
Contains the content. Extends [YStack](/docs/components/stacks).
#### <Handle />
Shows a handle above the frame by default, on tap it will cycle between `snapPoints` but this can be overridden with `onPress`.
Extends [XStack](/docs/components/stacks).
#### <Scrollview />
Allows scrolling within Sheet. Extends [Scrollview](/docs/components/scroll-view).
### Notes
For Android you need to manually re-propagate any context when using `modal`. This is because React Native doesn't support portals yet.
---
# Source: https://tamagui.dev/ui/slider.md
---
title: Slider
description: Drag to set values, vertically or horizontally.
name: slider
component: Slider
package: slider
demoName: Slider
---
```tsx hero template=Slider
```
## Installation
Slider is already installed in `tamagui`, or you can install it independently:
```bash
npm install @tamagui/slider
```
## Usage
Slider comes as multiple components that ship with default styles and are sizable. The `size` prop on `` will automatically pass size down to all the sub-components.
```tsx
import { Slider } from 'tamagui'
export default () => (
)
```
You can also optionally style any component, either using inline style props or by wrapping with `styled`:
```tsx
import { Slider, styled } from 'tamagui'
const CustomSliderTrack = styled(Slider.Track, {
backgroundColor: 'red',
})
export default () => (
)
```
## API Reference
### Slider
Contains every component for the slider.
void`,
description: `Called on slide start.`,
},
{
name: 'onSlideMove',
required: false,
type: `(event: GestureReponderEvent, value: number) => void`,
description: `Called on slide move.`,
},
{
name: 'onSlideEnd',
required: false,
type: `(event: GestureReponderEvent, value: number) => void`,
description: `Called on slide end.`,
},
]}
/>
### Slider.Track
`Slider.Track` Inherits `SizableStack`, extending all the default [props](/docs/intro/props).
### Slider.TrackActive
`Slider.TrackActive` Inherits `Stack`, extending all the default [props](/docs/intro/props).
### Slider.Thumb
`Slider.Thumb` Inherits `SizableStack`, extending all the default [props](/docs/intro/props), adding:
---
# Source: https://tamagui.dev/ui/spinner.md
---
title: Spinner
description: Render a loading indicator.
name: spinner
component: Spinner
demoName: Spinner
---
```tsx hero template=Spinner
```
## Installation
Spinner is already installed in `tamagui`, or you can install it independently:
```bash
npm install @tamagui/spinner
```
Note that due to the fact that Spinner is an extension of React Native [ActivityIndicator](https://reactnative.dev/docs/activityindicator), and that only accepts size `small` or `large`, we are currently limited to just these sizes.
```tsx
import { Button, Spinner } from 'tamagui'
export default () =>
```
## API Reference
### Spinner
Spinner extends [YStack](/docs/components/stacks), getting [Tamagui standard props](/docs/intro/props), plus:
---
# Source: https://tamagui.dev/docs/core/stack-and-text.md
---
title: View & Text
description: Your base components
---
View and Text are functionally equivalent to React Native `View` and `Text`, they just accept the superset of props that Tamagui supports.
### Props
See [the Props docs](/docs/intro/props) for the full list of properties View and Text accept.
### Usage
You can use them directly:
```tsx
import { View, Text } from 'tamagui' // or '@tamagui/core'
export default () => (
Hello
)
```
We encourage you to use inline styles. Combined with [shorthands](/docs/core/configuration#shorthands) they can lead to really easy styling, and the Tamagui optimizing compiler will take care of optimizing everything for you so that there is little to no extra cost in performance:
```tsx
import { View, Text } from 'tamagui' // or '@tamagui/core'
export default () => (
Hello
)
```
One really important and useful thing to note about Tamagui style properties: the order
is important! [Read more here](/docs/core/styled#order-is-important)
### With styled()
You can also use them [with styled](/docs/core/styled) to create your own lower level views that are meant to be re-usable:
```tsx
import { View, styled } from 'tamagui' // or '@tamagui/core'
export const Circle = styled(View, {
borderRadius: 100_000_000,
variants: {
pin: {
top: {
position: 'absolute',
top: 0,
},
},
centered: {
true: {
alignItems: 'center',
justifyContent: 'center',
},
},
size: {
'...size': (size, { tokens }) => {
return {
width: tokens.size[size] ?? size,
height: tokens.size[size] ?? size,
}
},
},
} as const,
})
```
Inline styles and `styled()` both are optimized by the compiler, so you can author styles using both depending on the use case.
---
# Source: https://tamagui.dev/ui/stacks.md
---
title: Stacks
description: An optional base for creating flex-based layouts.
name: stacks
component: Stacks
package: stacks
demoName: Stacks
---
```tsx hero template=Stacks
```
Tamagui UI includes optional stack views - XStack, YStack and ZStack. They extend directly off the [View](/docs/core/stack-and-text) from `@tamagui/core`.
Stack props accept [every prop from react-native-web](https://necolas.github.io/react-native-web/docs/view/) View, as well as all [the style properties Tamagui supports](/docs/intro/props).
In this example you'd show three `YStack` elements spaced out.
## Installation
Stacks is already installed in `tamagui`, or you can install it independently:
```bash
npm install @tamagui/stacks
```
```tsx
import { XStack, YStack } from 'tamagui'
export default () => (
)
```
To see all the style properties supported, see the [Props](/docs/intro/props) documentation.
### Fuller example
An example using a wide variety of style properties:
```tsx
import { Text, XStack, YStack } from 'tamagui'
export default () => (
HelloWorld
)
```
## API Reference
### XStack, YStack, ZStack
Beyond the [Tamagui Props](/docs/intro/props), the stacks add just two variants:
Sets position: absolute, top: 0, left: 0, right: 0, bottom: 0.
),
},
{
name: 'elevation',
required: false,
type: 'number | tokens.size',
description: (
Sets a natural looking shadow that expands out and away as the size gets bigger.
),
},
]}
/>
---
# Source: https://tamagui.dev/docs/core/styled.md
---
title: styled()
description: Extend and build custom and optimizable components
---
If you're looking for a full list of style properties accepted by Tamagui, see
the [Styles page](/docs/intro/styles).
Create a new component by extending an existing one:
```tsx
import { GetProps, View, styled } from '@tamagui/core'
export const RoundedSquare = styled(View, {
borderRadius: 20,
})
// helper to get props for any TamaguiComponent
export type RoundedSquareProps = GetProps
```
Usage:
```tsx
```
You can pass any prop that is supported by the component you are wrapping in styled.
One really important and useful thing to note about Tamagui style properties: the order is important! [Read more below](#order-is-important)
## Variants
Let's add some variants:
```tsx
import { View, styled } from '@tamagui/core'
export const RoundedSquare = styled(View, {
borderRadius: 20,
variants: {
pin: {
top: {
position: 'absolute',
top: 0,
},
},
centered: {
true: {
alignItems: 'center',
justifyContent: 'center',
},
},
size: {
'...size': (size, { tokens }) => {
return {
width: tokens.size[size] ?? size,
height: tokens.size[size] ?? size,
}
},
},
} as const,
})
```
Please use `as const` for the variants definition until Typescript gains the
ability to infer generics as const .
We can use these like so:
```tsx
```
To learn more about to use them and all the special types, [see the docs on variants](/docs/core/variants).
### Non-working React Native views
You can assume all "utility" views in React Native are not supported: Pressable, TouchableOpacity, and others. They have specific logic for handling events that conflicts with Tamagui. We could support these in the future, but we don't plan on it - you can get all of Pressable functionality for the most part within Tamagui itself, and if you need something outside of it, you can use Pressable directly.
## Using on the web
The `styled()` function supports Tamagui views, React Native views, and any other React component that accepts a `style` prop. If you wrap an external component that Tamagui doesn't recognize, Tamagui will assume it only supports the `style` prop and not optimize it.
If it does accept `className`, you can opt-in to className, CSS media queries, and compile-time optimization by adding `acceptsClassName`:
```tsx
import { SomeCustomComponent } from 'some-library'
import { styled } from 'tamagui' // or '@tamagui/core'
export const TamaguiCustomComponent = styled(SomeCustomComponent, {
acceptsClassName: true,
})
```
## styleable
Any component created with `styled()` has a new static property on it called `.styleable()`.
If you want a functional component that renders a Tamagui-styled component inside of it to *also* be able to be `styled()`, you need to wrap it with `styleable`. This is a mouthful, let's see an example:
```tsx
// 1. you create a `styled` component as usual:
const StyledText = styled(Text)
// 2. you create a wrapper component that adds some logic
// but still returns a styled component that receives the props:
const HigherOrderStyledText = (props) =>
// 3. you want that wrapper component itself to be able to use with `styled`:
const StyledHigherOrderStyledText = styled(HigherOrderStyledText, {
variants: {
// oops, variants will merge incorrectly
},
})
```
The above code will generally cause weird issues, because Tamagui can't know that it needs to just forward some props down. Instead, Tamagui tries to "resolve" all the style props from `StyledHigherOrderStyledText` before passing them down to `HigherOrderStyledText`. But that causes problems, because now `HigherOrderStyledText` will merge things differently than you'd expect.
The way to fix this is to add a `.styleable` around your `HigherOrderStyledText`. You'll also want to forward the ref, which is forwarded for you:
```tsx
const StyledText = styled(Text)
// note the styleable wrapper here:
const HigherOrderStyledText = StyledText.styleable((props, ref) => (
))
const StyledHigherOrderStyledText = styled(HigherOrderStyledText, {
variants: {
// variants now merge correctly
},
})
```
Now your component will handle everything properly, even if a theme is changed on `HigherOrderStyledText`, it will be applied.
A final note: you must pass all Tamagui style props given to `HigherOrderStyledText` down to a single `StyledText`, at least if you want everything to work fully correctly.
And if you'd like to add new props on top of the existing props, you can pass them in for the first generic type argument of styleable:
```tsx
import { View, ViewProps } from '@tamagui/core'
type ExtraProps = {
someCustomProp: boolean
}
export type CustomProps = ViewProps & ExtraProps
const Custom = View.styleable((props) => {
// ...
return null
})
```
### createStyledContext
When building a "Compound Component API", you need a way to pass properties down to multiple related components at once.
What is a Compound Component API? It looks like this:
```tsx
export default () => (
Lorem ipsum
)
```
Note how the `size="$large"` is set on the outer Button frame. We'd expect this size property to pass down to both the Icon and Text so that our frame size always matches the icon and text size. It would be cumbersome and bug-prone to have to always pass the size to every sub-component.
Tamagui solves this with `createStyledContext` which acts much like React `createContext`, except it only works with styled components and only controls their variants (for now, we're exploring if it can do more).
You can set it up as follows:
```tsx
import {
SizeTokens,
View,
Text,
createStyledContext,
styled,
withStaticProperties,
} from '@tamagui/core'
export const ButtonContext = createStyledContext<{
size: SizeTokens
}>({
size: '$medium',
})
export const ButtonFrame = styled(View, {
name: 'Button',
context: ButtonContext,
variants: {
size: {
'...size': (name, { tokens }) => {
return {
height: tokens.size[name],
borderRadius: tokens.radius[name],
gap: tokens.space[name].val * 0.2,
}
},
},
} as const,
defaultVariants: {
size: '$medium',
},
})
export const ButtonText = styled(Text, {
name: 'ButtonText',
context: ButtonContext,
variants: {
size: {
'...fontSize': (name, { font }) => ({
fontSize: font?.size[name],
}),
},
} as const,
})
export const Button = withStaticProperties(ButtonFrame, {
Props: ButtonContext.Provider,
Text: ButtonText,
})
```
A few things to note here:
- ButtonContext should only be typed and given properties that work across both components. Since they both define a `size` variant, this works.
- But note that one defines `...size` while the other defines `...fontSize`. This works in this case only if your design system has consistent naming for token sizes across `size` and `fontSize` (and is why we highly recommend this pattern).
- You can use `` now to set default props for a Button from above.
- As of today, using `context` pattern does not work with the optimizing compiler flattening functionality. So we recommend not using this for your most common components like Stacks or Text. But for Button or anything higher level it's totally fine - it will still extract CSS and remove some logic from the render function. We've mapped out how this can work with flattening eventually and it shouldn't be too much effort.
### Order is important
Finally, it's important to note that the order of style properties is significant. This is really important for two reasons:
1. You want to control which styles are overridden.
2. You have a variant that expands into multiple style properties, and you need to control it.
Lets see how it lets us control overriding styles:
```tsx
import { View, ViewProps } from '@tamagui/core'
export default (props: ViewProps) => (
)
```
In this case we set a default `background` to red, but it can be overridden by props. But we set `width` _after_ the prop spread, so width is _always_ going to be set to 200.
It also is necessary for variants to make sense. Say we have a variant `huge` that sets `scale` to 2 and `borderRadius` to 100:
```tsx
// this will be scale = 3
export default (props: ViewProps) => (
)
// this will be scale = 2
export default (props: ViewProps) => (
)
```
If order wasn't important, how would you expect these two different usages to work? You'd have to make order important _somewhere_. If you do it in the `styled()` helper somewhere, you end up having no flexibility and would end up with boilerplate. Making the prop order important gives us maximum expressiveness and is easy to understand.
---
## Advanced
You can skip this section unless you're building out very rich components that are nested multiple levels and need variants at each level.
### Custom props that accepts tokens with `accept`
If you are wrapping something like an SVG, you may want it to accept theme and token values on certain props, for example `fill`. You can do so using `accept`:
```tsx
const StyledSVG = styled(SVG, {}, {
accept: {
fill: 'color'
} as const
})
```
Note the `as const`, until we can drop TypeScript 4 support. Now, your StyledSVG will properly type the `fill` property to accept token and theme values and will pass the resolved colors to the SVG component.
You can also use `accept` to take in Tamagui style objects and output React Native style objects. This is useful for things like the `contentContainerStyle` prop on `ScrollView`, which expects a style object:
```tsx
const MyScrollView = styled(ScrollView, {}, {
accept: {
contentContainerStyle: 'style' // or 'textStyle'
} as const
})
```
---
# Source: https://tamagui.dev/ui/switch.md
---
title: Switch
description: A toggle between two states.
name: switch
component: Switch
package: switch
demoName: Switch
---
StyledUnstyledHeadless
```tsx hero template=Switch
````
```tsx hero template=SwitchUnstyled
````
```tsx hero template=SwitchHeadless
````
## Installation
Switch is already installed in `tamagui`, or you can install it independently:
```bash
npm install @tamagui/switch
````
Switch is already installed in `tamagui`, or you can install it independently:
```bash
npm install @tamagui/switch
```
To use the headless switch, you want to import it from the
`@tamagui/switch-headless` package. This package has no dependency on
`@tamagui/core`, but still works off the react-native APIs.
This means you can bring your own style library.
```bash
npm install @tamagui/switch-headless
```
## Usage
```tsx
import { Switch } from 'tamagui' // or '@tamagui/switch'
export default () => (
)
```
Using the `createSwitch` export, you can create an unstyled switch without using
any of the default styles. This is similar to the `unstyled` prop, but it
doesn't assume the props `size` or `unstyled` exist, and it won't automatically
apply the `active` theme.
You must pass `SwitchContext` as the `context` option to your Frame and Thumb
styled components.
If you define a `checked` variant, it will apply those styles.
```tsx template=SwitchUnstyled
```
Using the `useSwitch` API, you can make your own Switch from scratch.
```tsx template=SwitchHeadless
```
## API Reference
### Switch
`Switch` extends Stack views inheriting all the
[Tamagui standard props](/docs/intro/props), plus:
void',
},
{
name: 'unstyled',
type: 'boolean',
default: 'false',
description: `When true, remove all default tamagui styling.`,
},
{
name: 'native',
type: 'NativeValue<"mobile" | "ios" | "android">',
description: `Render to a native switch. (Not supported on web)`,
},
{
name: 'nativeProps',
type: 'SwitchProps (from `react-native`)',
description: `Props to pass to the native Switch;`,
},
]}
/>
### Switch.Thumb
`Switch.Thumb` extends Stack views inheriting all the
[Tamagui standard props](/docs/intro/props), plus:
{/* TODO: document createSwitch and useSwitch's API */}
---
# Source: https://tamagui.dev/ui/tabs.md
---
title: Tabs
description: Use in pages to manage sub-pages.
name: tabs
component: Tabs
package: tabs
demoName: Tabs
---
```tsx hero template=Tabs
```
Note: Tabs have landed on v1.7 and not fully ready for runtime. Send us your feedback and we'll address it. We're marking it Beta a such as there may be hopefully minimal breaking changes as we get feedback on the API.
## Usage
```tsx
import { SizableText, Tabs } from 'tamagui'
export default () => (
Tab 1Tab 2
Tab 1
Tab 2
)
```
## API Reference
### Tabs
Root tabs component. Extends [Stack](/docs/components/stack). Passing the `size` prop to this component will have effect on the descendants.
void',
description: `A function called when a new tab is selected`,
},
{
name: 'orientation',
type: '"horizontal" | "vertical"',
default: 'horizontal',
description: `The orientation the tabs are layed out`,
},
{
name: 'dir',
type: '"ltr" | "rtl"',
description: `The direction of navigation between toolbar items`,
},
{
name: 'activationMode',
type: '"manual" | "automatic"',
default: 'automatic',
description: `Whether or not a tab is activated automatically or manually. Not applicable on mobile`,
},
]}
/>
### Tabs.List
Container for the trigger buttons. Supports scrolling by extending [Group](/docs/components/group). You can disable passing border radius to children by passing `disablePassBorderRadius`.
### Tabs.Trigger
Extends [Button](/docs/components/button), adding:
void`,
description: `Used for making custom indicators when trigger interacted with`,
},
{
name: 'unstyled',
type: `boolean`,
description: `When true, remove all default tamagui styling`,
},
]}
/>
### Tabs.Content
Where each tab's content will be shown. Extends [ThemeableStack](/docs/components/stacks#themeablestack), adding:
## Examples
### Animations
Here is a demo with more advanced animations using [AnimatePresence](/docs/core/animations#animatepresence-and-exitstyle) and [Trigger](#trigger)'s `onInteraction` prop.
```tsx hero template=TabsAdvanced
```
---
# Source: https://tamagui.dev/ui/tamagui-image.md
---
title: Image
description: Web compatible and super light image component with Tamagui style props.
name: html
component: Image
package: image-next
demoName: WebNativeImageDemo
---
```tsx hero template=WebNativeImage
```
## Installation
Image is already installed in `tamagui`, or you can install it independently:
```bash
npm install @tamagui/image-next
```
## Usage
```tsx
export default () => (
)
```
## API Reference
### Image
[Tamagui props](/docs/intro/props) + [Web img props](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img) + [React Native Image props](https://necolas.github.io/react-native-web/docs/image/).
All web [img](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img)
props are supported on web, and all native
[Image](https://necolas.github.io/react-native-web/docs/image/) props are
supported on native. on native we are still using web img APIs, but not all
web img props are supported. like `decoding`
#### Some common props
---
# Source: https://tamagui.dev/ui/text.md
---
title: Text
description: Text, Sized Text and Paragraph show one way to build a design system.
name: text
component: Paragraph
demoName: Text
---
# Text
Text, Sized Text and Paragraph show one way to build a design system.
```tsx hero template=Text
```
### Usage
Text in Tamagui matches to Text in react-native-web, just with the added [Tamagui Props](/docs/intro/props).
It explicitly doesn't inherit your theme color or other font properties, as it's meant to be plain and used for extension. Below, we'll show `SizableText` which extends Text, and `Paragraph` which extends SizableText. Generally, Paragraph is the useful view as it will use theme values, while you can extend Text if you'd like to derive your own design system.
```tsx
import { Text, XStack, YStack } from 'tamagui'
export default () => (
<>
Lorem ipsum
>
)
```
SizableText and Paragraph default to the "body" fontFamily defined in your config.
Headings all default to "heading".
## SizableText
[Seeing how SizableText is defined](https://github.com/tamagui/tamagui/blob/master/packages/tamagui/src/views/SizableText.tsx) is helpful for understanding Tamagui. They serve as a good example of how you can extend and compose components.
SizableText simply adds a single `size` property to maniplulate all of:
- fontSize
- lineHeight
- fontWeight
- letterSpacing
Based on the values set in your tokens. It uses [spread variants](/docs/core/styled#spread-variants) feature. Then Paragraph extends that and ensures it defaults to values from your theme - fontSize, lineHeight, color and fontFamily.
## Paragraph
Finally Paragraph extends `SizableText` and simply sets some default values from your theme:
```tsx
export const Paragraph = styled(SizableText, {
fontFamily: '$body',
color: '$color',
// note tamagui uses a generic "true" token that your sizes should set to be the same as the default on your scale
size: '$true',
})
```
---
# Source: https://tamagui.dev/docs/core/theme.md
---
title: Theme
description: Change themes contextually
---
The package `tamagui` is a superset of `@tamagui/core`. If you are using `tamagui`, be sure to
import Theme from there - there's no need to add core to your package.json.
Changing themes in Tamagui is as easy as passing their name to the Theme component.
### Usage
Change themes anywhere in your app like so:
```tsx
import { Button, Theme } from 'tamagui' // or '@tamagui/core'
export default () => (
I'm a dark button
)
```
### Sub-themes
There is one special property of themes that's very helpful for what is essentially sub-themes, or tints.
Basically, if you have a base `dark` theme and a `light` theme, you may want to have "tinted" versions of those themes. For example `light_pink` and `dark_pink`. Instead of having hard-coded color values like `$pink3`, you create as many pink themes as you want: `light_pink_alt1`, `light_pink_alt2`, etc.
Each of those themes is recommended to be small: just having values that map to ViewStyle or TextStyle, like `$color` (and that can postfix pseudo states, like `$colorHover`).
Then, you just give your component a `theme="pink"` prop, or wrap it in ``. Note you can leave out the `light_` or even `light_pink` prefix, Tamagui will nest properly so long as the parent is set.
It's easier to understand with an example. First, define your tokens and themes:
```tsx
import { createTamagui, createTokens } from 'tamagui'
const tokens = createTokens({
color: {
pinkDark: '#610c62',
pinkLight: '#f17efc',
},
// ... see configuration docs for required tokens
})
export default createTamagui({
tokens,
themes: {
dark: {
background: '#000',
color: '#fff',
},
light: {
color: '#000',
background: '#fff',
},
dark_pink: {
background: tokens.color.pinkDark,
color: tokens.color.pinkLight,
},
light_pink: {
background: tokens.color.pinkLight,
color: tokens.color.pinkDark,
},
},
})
```
Using the `_pink` suffix, the `Theme` component will now let us automatically do sub-theming:
```tsx
import { Button, Theme } from 'tamagui'
export default () => {
return (
I have the theme darkI have the theme pink-dark
)
}
```
Notice we just use the name `pink`. Because theres no `pink` theme, the Theme component will automatically check if theres a theme that matches the pattern `[currentThemeName]_[givenName]`, in this case `dark_pink`. This is really useful for things like having an `active` or `error` theme that match your parent theme styles.
---
## Inverse
The `inverse` prop will change any dark theme to light, and vice-versa. If it's a sub-theme, it'll check for matching parents, so `dark_red` will inverse to `light_red` if possible:
```tsx hero template=ThemeInverse
```
## Reset
The `reset` prop will change the theme to the grandparent's value:
```tsx
{/* This square and all children will have theme "dark" */}
```
---
## themeable()
The `themeable` helper function is a "higher order component" that wraps any React component definition and adds two props:
- `theme` for changing the theme on that component and its children
- `themeInverse` which wraps the component with ``
It's used for example on [Button](/docs/components/button).
---
# Source: https://tamagui.dev/ui/toast.md
---
title: Toast
description: A toast component with native features
name: toast
component: Toast
package: toast
demoName: Toast
---
# Toast Use to show feedback to user interactions
```tsx hero template=Toast
```
## Usage
### Imperative API
This API offers a nice imperative interface, support for native toasts with a lot of control.
#### Installation
For native support on mobile, run `yarn add burnt` to add `burnt`, then rebuild your React Native app. React Native requires sub-dependencies with native dependencies always be hoisted to your apps package.json and Toast relies on the amazing [Burnt](https://github.com/nandorojo/burnt) library by Fernando Rojo to provide its native functionality.
Note: `Burnt` [will not work](https://github.com/nandorojo/burnt?tab=readme-ov-file#expo) with Expo Go.
#### Usage
To display the toast natively, you should either pass an array of native platforms (`native: ["ios", "web"]`), a single platform or `true` for all platforms.
```tsx
import { Button } from 'tamagui' // or '@tamagui/button'
import { Toast, ToastImperativeProvider, ToastProvider, useToast } from 'tamagui' // or '@tamagui/toast'
const options = { native: 'mobile' }
export default () => (
)
const CurrentToast = () => {
const { currentToast } = useToast()
// only show the component if it's present and not handled by native toast
if (!currentToast || currentToast.isHandledNatively) return null
return (
{currentToast.title}{currentToast.message}
)
}
const MyPage = () => {
const { show } = useToast()
return (
show('Done!', { message: 'Form submitted successfully.' })}>
Show Toast
)
}
```
### Barebone API
This API does not support native toasts. For native toasts, use the [Imperative
API](#imperative-api).
#### Single Toast
```tsx
export default () => {
const [open, setOpen] = React.useState(false)
const timerRef = React.useRef(0)
React.useEffect(() => {
return () => clearTimeout(timerRef.current)
}, [])
return (
{
setOpen(false)
window.clearTimeout(timerRef.current)
timerRef.current = window.setTimeout(() => {
setOpen(true)
}, 150)
}}
>
Single Toast
Subscribed!We'll be in touch.
)
}
```
#### Multiple Toast
To use multiple toasts, you should pass `multipleToasts` to your `ToastViewport`.
Otherwise there'll be issues when swipe-dismissing or animating toasts.
```tsx
export default () => {
const [savedCount, setSavedCount] = React.useState(0)
return (
{
setSavedCount((old) => old + 1)
}}
>
Show toast
{[...Array(savedCount)].map((_, index) => (
Subscribed!We'll be in touch.
))}
)
}
```
## API
### ToastProvider
Your toasts should be wrapped within a `ToastProvider`. This is usually done at the root of your application.
### ToastViewport
The portal for toasts to be directed to. Should be inside [ToastProvider](#toastprovider). Beyond [Stack Props](/docs/components/stacks/1.0.0), adds:
### Toast
Contains the Title, Description, Action and Close component. Should be inside [ToastProvider](#toastprovider). Extends [Stack](/docs/components/stack#api) and adds:
#### Toast.Title
Should be inside [Toast](#toast). Extends [SizableText](/docs/components/text/1.0.0#sizabletext).
#### Toast.Description
Should be inside [Toast](#toast). Extends [SizableText](/docs/components/text/1.0.0#sizabletext).
#### Toast.Close
Should be inside [Toast](#toast). Extends [Stack](/docs/components/stacks/1.0.0). You can pass `asChild` to this component and use a custom `` inside.
#### Toast.Action
Should be inside [Toast](#toast). Extends [Stack](/docs/components/stacks/1.0.0). You can pass `asChild` to this component and use a custom `` inside.
### ToastImperativeProvider
Wrap components within this provider to use `useToast()` inside them.
### useToast
Used when using the imperative API.
## FAQ
#### How to change the placement of toasts?
##### Native toasts
- iOS (burnt): Supports top or bottom placements. Adjustable by passing `from` to `burntOptions`:
```tsx
```
- Android (burnt): Not supported.
- Web (Notification API): Not supported.
##### Non-native toasts
You should change the positioning of your [``](#toastviewport). For instance, if you want them to appear from top right:
```tsx
```
Or for bottom center:
```tsx
```
When using multiple toasts, you can change the order of toasts by setting
`flexDirection` to `column` or `column-reverse`. Or even have them stack horizontally
using `row` or `row-reverse`.
#### How to show non-native toasts within safe area on mobile?
Install `react-native-safe-area-context` if you haven't, wrap your app inside ``, and use the safe area insets to position the viewport inside the safe area.
```tsx
import { useSafeAreaInsets } from 'react-native-safe-area-context'
const SafeToastViewport = () => {
const { left, top, right } = useSafeAreaInsets()
return (
)
}
```
#### Can I have multiple viewports?
Yes, but you will have to name them and then reference the viewport name on the `` component. for instance:
```tsx
const App = () => {
return (
// name will be "default"
)
}
const MyComponent = () => {
return // goes to default viewport // ...
}
const MyComponent2 = () => {
return // ...
}
```
---
# Source: https://tamagui.dev/ui/toggle-group.md
---
title: ToggleGroup
description: Two-state buttons that can be toggled on or off.
name: toggleGroup
component: ToggleGroup
package: toggle-group
demoName: ToggleGroup
---
```tsx hero template=ToggleGroup
```
## Installation
ToggleGroup is already installed in `tamagui`, or you can install it independently:
```bash
npm install @tamagui/toggle-group
```
## Usage
```tsx
import { ToggleGroup } from 'tamagui'
export default () => {
return (
)
}
```
## API Reference
### ToggleGroup
`ToggleGroup` extends the [Group](/docs/components/group) component. You can disable passing border radius to children by passing `disablePassBorderRadius`. plus:
void',
description: `Event handler called when the pressed state of an item changes and type is "multiple".`,
},
{
name: 'loop',
type: 'boolean',
description: `Whether or not to loop over after reaching the end or start of the items. Used mainly for managing keyboard navigation.`,
default: `true`,
},
{
name: 'disableDeactivation',
type: 'boolean',
description: `Won't let the user turn the active item off. Only applied to single toggle group.`,
default: 'false',
},
{
name: 'unstyled',
type: 'boolean',
default: 'false',
description: `When true, remove all default tamagui styling.`,
},
{
name: 'sizeAdjust',
type: 'number',
description: `Adjust the component's size scaling by this number.`,
},
{
name: 'toggledStyle',
type: 'object',
description: `Styles to apply to all items when they are in the pressed/active state. This is passed down via context to all ToggleGroup.Item children.`,
},
]}
/>
### ToggleGroup.Item
`ToggleGroup.Item` extend Stack views inheriting all the [Tamagui standard props](notion://www.notion.so/docs/intro/props), plus:
When it is active, it will receive an `active` prop set to true. You can customize active styles in two ways:
### Using toggledStyle prop
The simplest way to customize the active state is with the `toggledStyle` prop:
```tsx
import { ToggleGroup } from '@tamagui/toggle-group'
// Apply to all items via the parent
AB
// Or apply to individual items
AB
```
You can also use `toggledStyle` when creating styled components:
```tsx
import { ToggleGroup } from '@tamagui/toggle-group'
import { styled } from 'tamagui'
const MyToggleGroupItem = styled(ToggleGroup.Item, {
toggledStyle: {
backgroundColor: '$green9',
color: '$yellow9',
},
})
```
### Using the active variant
For more complex styling needs, you can use the `active` variant:
```tsx
import { ToggleGroup } from '@tamagui/toggle-group'
import { styled } from 'tamagui'
const MyToggleGroupItem = styled(ToggleGroup.Item, {
variants: {
active: {
true: {
backgroundColor: 'red'
},
},
},
})
```
---
# Source: https://tamagui.dev/docs/core/tokens.md
---
title: Tokens
description: Accessing and using tokens
---
Tamagui lets you create tokens using `createTokens`, which is then passed to the `createTamagui` function as part of the [configuration](/docs/core/configuration) object.
### Getting tokens
For example if you define some tokens:
```tsx
const tokens = createTokens({
size: {
small: 10
}
})
```
After you pass that into `createTamagui`, you can access your tokens from anywhere using `getTokens`:
```tsx
import { getTokens } from '@tamagui/core'
getTokens().size.small
```
or
```tsx
getTokens().size['$small']
```
If you'd like just an object containing the prefixed (starting with `$`) or non-prefixed values, you can use the `prefixed` option:
```tsx
// only non-$
getTokens({ prefixed: false }).size.small
// only $
getTokens({ prefixed: true }).['$size'].small
```
What is returned is of type `Variable`, which is what Tamagui turns all tokens and theme values into internally in order to give them CSS variable names, as well as other things:
```tsx
getTokens().size.small.val // returns 10
getTokens().size.small.variable // returns something like (--size-small), which matches the CSS rule inserted
```
Tamagui has some helpers that make working with variables easier, which are documented in [Exports](/docs/core/exports), namely [`getVariable`](/docs/core/exports#getvariable) which will return the CSS variable on web, but raw value on native, and `getVariableValue` which always returns the raw value.
#### Color tokens as fallback values for themes
Color tokens are available as fallback values when you access a theme. So when you `useTheme()` and then access a value that isn't in the theme, it will check for a `tokens.color` with the matching name.
Think of it this way:
- Tokens are static and are your base values.
- Themes are dynamic, they can change in your React tree, and live above tokens.
If you are confused by Tamagui themes, don't be. You can avoid them altogether, or avoid learning them until later. Instead, you can just build your app using regular style props and leave out themes altogether. Or, simply use a `light` and a `dark` theme if you want light and dark mode in your app, but avoid using any nested themes.
### Using tokens with components
When using `styled` or any Tamagui component like `Stack`, you can access tokens directly. Just like with `useTheme`, it will first look for a theme value that matches, and if not it will fall back to a token.
Tokens are automatically applied to certain properties. For example, `size` tokens are applied to width and height. And of course `radius` to borderRadius.
Here's how they all apply:
### Specific tokens
As of version 1.34, you can also define any custom token values you'd like:
```tsx
const tokens = createTokens({
// ...other tokens
icon: {
small: 16,
medium: 24,
large: 32,
},
})
```
And then access them using the new "specific tokens" syntax:
```tsx
export default () => (
)
```
This, like all token values, works the same with `styled`:
```tsx
import { styled, Stack } from '@tamagui/core'
export const MyStack = styled(Stack, {
width: '$icon.small'
})
```
When creating custom tokens, you can use the `px` helper to ensure values get proper pixel units on web while remaining as raw numbers on native:
```tsx
import { createTokens, px } from '@tamagui/core'
const tokens = createTokens({
customSize: {
small: px(100), // → "100px" on web, 100 on native
medium: px(200),
large: px(300),
},
opacity: {
low: 0.25, // → 0.25 (unitless on both platforms)
medium: 0.5,
high: 0.75,
},
})
```
The predefined token categories like `size`, `space`, and `radius` automatically add pixel units where appropriate, so you don't need to use the `px` helper for them.
---
# Source: https://tamagui.dev/ui/tooltip.md
---
title: Tooltip
description: A tooltip on web, with only accessibility output on native.
name: tooltip
component: Tooltip
package: tooltip
demoName: Tooltip
---
```tsx hero template=Tooltip
```
Note that Tooltip doesn't render on native platforms.
## Installation
Tooltip is already installed in `tamagui`, or you can install it independently:
```bash
npm install @tamagui/tooltip
```
### PortalProvider
If you are not using `tamagui` (but rather `@tamagui/core`) you'll need to install the `@tamagui/portal` package:
```bash
npm install @tamagui/portal
```
Then add `PortalProvider` to the root of your app:
```tsx fileName="App.tsx"
import { PortalProvider } from '@tamagui/portal'
import YourApp from './components/YourApp'
function App() {
return (
)
}
export default App
```
## Anatomy
```tsx
import { Tooltip } from 'tamagui' // or '@tamagui/tooltip'
export default () => (
{/* ... */}
)
```
## API Reference
### Tooltip
Contains every component for the tooltip.
void',
required: false,
},
{
name: 'modal',
type: 'boolean',
default: 'true',
required: false,
description: `Renders into root of app instead of inline`,
},
{
name: 'stayInFrame',
type: 'ShiftProps',
required: false,
description: `See floating-ui shift()`,
},
{
name: 'allowFlip',
type: 'FlipProps',
required: false,
description: `See floating-ui flip`,
},
{
name: 'offset',
type: 'OffsetOptions',
required: false,
description: `Determines the distance the Popover appears from the target, see floating-ui offset().`,
},
]}
/>
If using `modal={true}` (which is `true` by default), refer to the [PortalProvider installation](#portalprovider) for more information.
### Tooltip.Trigger
Used to trigger opening of the popover when uncontrolled, see YStack in [Stacks](/docs/components/stacks).
### Tooltip.Content
Renders as SizableStack (see [Stacks](/docs/components/stacks)) with an extra `size` prop that accepts any `SizeTokens`.
### Tooltip.Anchor
Renders as YStack, see [Stacks](/docs/components/stacks).
When you want the Trigger to be in another location from where the Tooltip attaches, use Anchor. When used, Anchor is where the Tooltip will attach, while Trigger will open it.
### TooltipGroup
This allows you to logically group any tooltips rendered below this component. You can control their delay props, and components inside a TooltipGroup will be smart about opening quicker if you are moving between targets with tooltips, ensuring that subsequent tooltips show immediately rather than after a delay.
See the [Floating UI docs](https://floating-ui.com/docs/floatingdelaygroup) for full details on how this works.
### closeOpenTooltips
A small helper function of type `() => void` that will close any open tooltips.
---
# Source: https://tamagui.dev/ui/unspaced.md
---
title: Unspaced
description: Avoids spacing for children inside a spacing container.
name: html
component: Unspaced
---
While the `space` property is deprecated, Unspaced lives on in a better form, one which is easily back-ported to add Unspaced support (as well as adding `gap` support).
When using the `space` style prop, you may want some children to not be spaced.
Use Unspaced:
```tsx
import { Text, Unspaced, View } from '@tamagui/core'
export default () => (
{/* space */}
{/* no */}
Some absolute positioned text
)
```
If you want the item to be visually hidden as well as unspaced, see
[VisuallyHidden](/docs/components/visually-hidden), which uses Unspaced but also hides the contents in an accessible manner.
---
# Source: https://tamagui.dev/docs/core/use-media.md
---
title: useMedia
description: Respond to different screen sizes
---
Define your media rules in the media object of your `tamagui.config.ts`:
```tsx line=2-6
export default createTamagui({
media: {
xs: { maxWidth: 660 },
gtXs: { minWidth: 660 + 1 },
sm: { maxWidth: 860 },
gtSm: { minWidth: 860 + 1 },
md: { maxWidth: 980 },
gtMd: { minWidth: 980 + 1 },
lg: { maxWidth: 1120 },
gtLg: { minWidth: 1120 + 1 },
short: { maxHeight: 820 },
tall: { minHeight: 820 },
hoverNone: { hover: 'none' },
pointerCoarse: { pointer: 'coarse' },
},
})
```
Choose the naming convention you prefer. We use `sm`, `md`, `lg` and `gtSm` etc. Where "gt" means "greater than".
The order here important: items defined further down will override items before. In this example, `xs` is the weakest, `gtXs` will override it, and so on.
Behind the scenes, we convert this object syntax into media query syntax with a simple loop over your object, turning camelCase into hyphen-case, and adding `@media()` around it.
### Usage
Now in any component you may import and use `useMedia` or `$` prefixed media prop styles.
### Inline props
```tsx
import { Button, XStack, useMedia } from 'tamagui' // note: design system can use @tamagui/core
export default () => {
const [x, setX] = useState(0)
return (
0.5 ? 'green' : 'yellow',
}}
>
setX(Math.random())}>Hello
)
}
```
In this example we are doing mobile-first design, where the base props will be overridden as the viewport gets wider. Notice the ternary logic in `$gtMd`: this is extractable with Tamagui, and will still output simple CSS with no leftover style props in your component's render function.
### Hooks
```tsx
import { Button, XStack, useMedia } from 'tamagui' // note: design system can use @tamagui/core
export default () => {
const media = useMedia()
return (
Hello
)
}
```
As long as **all** of your usages of useMedia are extractable, Tamagui will actually generate your CSS and then fully remove the hook from the output code. You can check this by adding `// debug` to the top of your component.
Of course, you may sometimes use `useMedia` for other purposes than styling. In fact, that's the nice part about Tamagui - if it can't extract, it always falls back to runtime gracefully.
Tamagui runtime `useMedia` usage will track which keys are accessed, and only update if the media query matching that key updates. This granular updating is nice for performance.
#### Limitations
The `useMedia` hook uses proxies so it can track which keys you are accessing and only re-render components that need re-rendering based on those keys. The Tamagui compiler also understands straightforward uses of the hook during compile-time and can remove it entirely when targeting the web, in favor of CSS.
- In order to keep things lightweight and simple, the proxied object returned by `useMedia` is not iterable, and you can't check keys on it using `in` or get them using `Object.keys`.
- It's encouraged to write `const media = useMedia()` and then access your keys like `media.sm` to work best with the compiler. The compiler supports de-structuring, but not re-naming.
---
# Source: https://tamagui.dev/docs/core/use-theme.md
---
title: useTheme
description: Creating and using theme values
---
Access the current theme in context with `useTheme`. Tamagui themes operate much
the same as
[CSS variables](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties)
do, and so
[they can nest and override each other contextually](/docs/intro/themes).
Note that the object that useTheme returns is the current theme, proxied upwards
to every parent theme, finally proxying back to your tokens. This means it
behaves just like CSS variables at well, at runtime.
A short example:
```tsx
import { YStack, useTheme } from 'tamagui'
const App = () => {
const theme = useTheme()
return
}
```
The `useTheme` hook returns your Theme turned into a `ThemeParsed`, which means
it turns all values into a `Variable`:
```tsx
{
background: {
val: '#000',
variable: 'var(--background)',
name: 'background',
isVar: true,
},
color: {
val: '#fff',
variable: 'var(--color)',
name: 'color',
isVar: true,
},
}
```
The `useTheme` hook further adds a `.get()` helper function on each Variable for
extra performance. On the web, this will return the `.variable` property and
avoid re-rendering if values change, as it assumes you are using the CSS
variables it generates to update styles. Meanwhile on native it will return
`.val` and re-render always. But - if you enable the
[`fastSchemeChange` setting on createTamagui](/docs/core/configuration#settings)
then `get()` will return a
[DynamicColorIOS](https://reactnative.dev/docs/dynamiccolorios) on iOS and avoid
re-rendering there too - unless you do `get('web')` in which case it will only
optimize for web.
```tsx
import { Stack, useTheme } from '@tamagui/core'
import { SomeExternalComponent } from 'some-external-component'
const App = () => {
const theme = useTheme()
// on the web this is something like var(--background) and will avoid re-renders
// on native it will be something like #fff and will re-render
const background = theme.background.get()
// if you needed to access it in a way that always returns the raw value
const backgroundValue = theme.background.val
return (
)
}
```
You can mix and match `useMedia` and `useTheme`, and the compiler understands
most basic usages, even with nested logic or constants within the file or
imports [white-listed in your build setup](/docs/compiler/install):
```tsx
import { YStack, useMedia, useTheme } from 'tamagui'
const App = () => {
const theme = useTheme()
const media = useMedia()
return (
)
}
```
This will compile on the web to:
```tsx
const _cn =
' _alignItems-1oszu61 _boxSizing-deolkf _display-6koalj _flexBasis-1mlwlqe _flexDirection-eqz5dr _flexShrink-1q142lx _transform-_sm_1exagq _transform-_sm0_1wpzndr _backgroundColor-_lg_no4z4g _backgroundColor-_lg0_1qoifqd _transform-_xl_gqa6p0'
import { YStack, useMedia, useTheme } from 'tamagui'
const App = () => {
return
}
```
And the following CSS:
```css
._alignItems-1oszu61 {
-ms-flex-align: stretch;
-webkit-align-items: stretch;
-webkit-box-align: stretch;
align-items: stretch;
}
._boxSizing-deolkf {
box-sizing: border-box;
}
._display-6koalj {
display: -webkit-box;
display: -moz-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
}
._flexBasis-1mlwlqe {
-ms-flex-preferred-size: auto;
-webkit-flex-basis: auto;
flex-basis: auto;
}
._flexDirection-eqz5dr {
-ms-flex-direction: column;
-webkit-box-direction: normal;
-webkit-box-orient: vertical;
-webkit-flex-direction: column;
flex-direction: column;
}
._flexShrink-1q142lx {
-ms-flex-negative: 0;
-webkit-flex-shrink: 0;
flex-shrink: 0;
}
@media (max-width: 860px) {
:root:root ._transform-_sm_1exagq {
-webkit-transform: translateY(10px);
transform: translateY(10px);
}
}
@media not all and (max-width: 860px) {
:root:root ._transform-_sm0_1wpzndr {
-webkit-transform: translateY(0px);
transform: translateY(0px);
}
}
@media (min-width: 1120px) {
:root:root:root ._backgroundColor-_lg_no4z4g {
background-color: var(--red);
}
}
@media not all and (min-width: 1120px) {
:root:root:root ._backgroundColor-_lg0_1qoifqd {
background-color: var(--blue);
}
}
@media (min-width: 1280px) {
:root:root:root:root ._transform-_xl_gqa6p0 {
-webkit-transform: translateY(var(--space2));
transform: translateY(var(--space2));
}
}
```
## Using outside of styling
You can `useTheme()` (and `useMedia()`) at runtime. Like useMedia, useTheme will
only re-render when it has to, and often you can skip re-renders altogether by
either passing the values to a Tamagui styled component, or by using the helper
`getVariable`:
### getVariable
If you access `theme.bg.val` in your render function, the component will only
re-render when theme.bg changes.
```tsx
import { theme, View } from '@tamagui/core'
export default () => {
const theme = useTheme()
// access the value
console.log(theme.bg.val)
return (
)
}
```
## Changing the theme at the hook level
This is a more advanced use for building custom hooks or components, but you can
pass in a theme name and a component theme name (as
[discussed here](/docs/intro/themes#component-subset-themes)) to grab the right
subset theme:
```tsx
function MyComponent(props) {
const theme = useTheme(props.theme, 'MyComponent')
}
```
For example, if you have the following themes:
- `dark`
- `dark_green`
- `dark_green_MyComponent`
And this code:
```tsx
```
The above `useTheme` hook would return the `dark_green_MyComponent` theme.
---
# Source: https://tamagui.dev/docs/core/variants.md
---
title: Variants
description: Simple typed prop styles through styled()
---
Variants allow for a nice balance between simplicity and power, with affordances
for both the compiler and the type system.
In a style library you want to be able to succinctly add on conditional values
that expand into groups of styles. Variants do just that. Before explaining more
on why and how they work, an example:
```tsx
import { View, styled } from 'tamagui' // or '@tamagui/core'
export const Circle = styled(View, {
borderRadius: 100_000_000,
variants: {
pin: {
top: {
position: 'absolute',
top: 0,
},
},
centered: {
true: {
alignItems: 'center',
justifyContent: 'center',
},
},
size: {
'...size': (size, { tokens }) => {
return {
width: tokens.size[size] ?? size,
height: tokens.size[size] ?? size,
}
},
},
} as const,
})
```
Notice the `as const` on the variant definition object. This is necessary to
please TypeScript until it gains the ability to infer constant objects. If
left out, your types may break.
We can use this styled component like so:
```tsx
```
This component uses a few different types of variants. Below we'll expand on
each.
But first, why variants?
## Why Variants?
You have two basic options for sharing styles across components: share style
objects and then combine them directly into your render function, or allow
abstraction in some form.
You can think of `StyleSheet.create` as "a lot of style objects". While this is
nice in that it's simple, it doesn't enforce any rules, which can get you in
trouble with both a compiler and a type system.
You end up basically typing props by hand and then doing somewhat arbitrary
logic to glue it all together inside a functional component. The types may not
map to the actual output, and a compiler will almost certainly get confused or
be unable to optimize with any easily unforeseen abstraction.
Variants force you out of the React render function, which means no hooks and a
much clearer contract of limitations: at most you take in a value, your design
system, and props, and you output a group of styles.
It's nice having this special area just for styling. It keeps your types correct
by definition. And it ensures the optimizing compiler can understand your styled
components and people on your team won't "de-opt" it on accident.
And because Variants work with the styled function, they nest without adding an
extra depth to your render tree. Doing `styled(styled())` results in a single
React component, and can be optimized and flattened by the compiler as well.
Whereas it's quite easy for developers to take an existing functional component
and throw in a new one around it, further de-optimizing a compiler and leading
to a less clear separation of styled components and regular ones.
## Variants
### Typed Variants
#### `true` or `false`
The special keys `true` and `false` will map to a boolean. So the `centered`
prop will be typed to accept true or false, and when true it will apply it's
styles.
```tsx
import { View, styled } from 'tamagui' // or '@tamagui/core'
export const MyView = styled(View, {
variants: {
selectable: {
true: {
userSelect: 'auto',
},
false: {
userSelect: 'none',
},
},
} as const,
})
```
#### String, Boolean, Number Variants
You can use a pseudo Typescript syntax for other variants:
- `:string` - Accepts a `string`
- `:boolean` - Accepts a `boolean` (less precedence than `true` or `false`)
- `:number` - Accepts a `number`
```tsx
import { View, styled } from 'tamagui' // or '@tamagui/core'
export const ColorfulView = styled(View, {
variants: {
color: {
':string': (color) => {
// color is of type "string"
return {
color,
borderColor: color,
}
},
},
} as const,
})
```
### Spread Variants
When you write variants, you have to be explicit so TypeScript and the runtime
know exactly which props you accept. This can be especially cumbersome when you
want to "gather" all the values of a specific token. For example, without spread
variants, if you wanted to have a `pad` property that accepted all the keys from
`tokens.size`, you'd have to write this:
```tsx
// in your tamagui.config.ts:
const tokens = createTokens({
size: {
sm: 10,
md: 15,
lg: 25,
// ...
}
// ... see configuration docs for required tokens
})
export default createTamagui({
tokens
})
// somewhere in your app:
const MyButton = styled(View, {
variants: {
pad: {
sm: {
padding: tokens.size.sm,
},
md: {
padding: tokens.size.md,
},
lg: {
padding: tokens.size.lg,
},
// ...
}
} as const
})
// now you can
```
This is verbose, and only gets more verbose if you add more sizes. It would
require always updating every component every time you change the tokens.
Spread variants solve this problem. Instead, we can write:
```tsx
// in your tamagui.config.ts:
const tokens = createTokens({
size: {
sm: 10,
md: 15,
lg: 25,
// ...
}
// ... see configuration docs for required tokens
})
export default createTamagui({
tokens
})
// somewhere in your app:
const MyButton = styled(View, {
variants: {
pad: {
'...size': (val, { tokens }) => ({
padding: tokens.size[val]
}),
}
} as const
})
// now you can
```
Spread variants save you from having to define hardcoded styles for every key
(`sm`, `md`, `lg`) in your token object. They collect values from any of your
top level token categories. So you can only use `...color`, `...size`,
`...space`, `...font`, `...fontSize`, `...lineHeight`, `...radius`,
`...letterSpace`, or `...zIndex`. They must be prefixed with `...` as that is
how they are typed properly and assembled for runtime.
#### Extra properties passed to functional variants
There's a second argument passed to all variant functions that is a
bag-o-goodies that help you use the current tokens, theme, props, and fonts
easily.
```tsx
const SizableText = styled(Text, {
variants: {
size: {
'...size': (size, { tokens, font }) => {
return {
fontSize: font?.size,
lineHeight: font?.lineHeight,
height: tokens.size[size] ?? size,
}
},
},
} as const,
})
```
Which you can use:
```tsx
Hello world
```
The Spread variant function will receive two arguments: the first is the value
given to the property (`"$lg"`), and the second is an object with
`{ theme, tokens, props, font }`.
#### Catch-all variants
Much like a dynamic variant, except it lets you use it alongside the other typed
variants you need. Use '...' and it will grab all variants that don't match:
```tsx
import { View, styled } from 'tamagui' // or '@tamagui/core'
export const ColorfulView = styled(View, {
variants: {
colorful: {
true: {
color: 'red',
},
'...': (val: string) => {
// this will catch any other values that don't match
return {
color: val,
}
},
},
} as const,
})
```
#### Dynamic variants
If you need more complex types, or simply prefer a shorter syntax, you can use a
single function instead of using the object syntax for variants:
```tsx
import { View, styled } from 'tamagui' // or '@tamagui/core'
export const MyView = styled(View, {
variants: {
doubleMargin: (val: number) => ({
margin: val * 2,
}),
} as const,
})
```
Tamagui also provides a few types of other variant definition patterns that work
with tokens or types.
### defaultVariants
Sometimes you'd like to set a default value for a variant you've just set on
your styled() component. Due to the way Typescript types parse from left to
right, we can't properly type variants directly on the object you define them
on.
The `defaultVariants` option allows you to set these, properly typed:
```tsx
const Square = styled(View, {
variants: {
size: {
'...size': (size, { tokens }) => {
// size === '$lg'
// tokens.size.$lg === 25
return {
width: tokens.size[size] ?? size,
height: tokens.size[size] ?? size,
}
},
},
} as const,
// will get size '$10' from size tokens automatically
defaultVariants: {
size: '$10',
},
})
```
### Variants and Pseudos, Media Queries
Variants have the full power of the Tamagui styling system, including pseudo and
media styles:
```tsx
const SizedText = styled(Text, {
variants: {
size: {
md: {
fontSize: '$sm',
$gtMd: {
fontSize: '$md',
},
$gt2xl: {
fontSize: '$lg',
},
},
},
} as const,
})
```
### Variants and Parent Variants
Styled components can access their parent components variants, even in their
variants:
```tsx
const ColorfulText = styled(Text, {
variants: {
colored: {
true: {
color: '$color',
},
},
large: {
true: {
fontSize: '$8',
},
},
} as const,
})
const MyParagraph = styled(ColorfulText, {
colored: true,
variants: {
hero: {
true: {
large: true,
},
},
} as const,
})
```
---
# Source: https://tamagui.dev/ui/visually-hidden.md
---
title: Visually Hidden
description: Hide content accessibly.
name: html
component: VisuallyHidden
---
VisuallyHidden hides an item but ensures it remains visible to accessibility readers.
## Installation
VisuallyHidden is already installed in `tamagui`, or you can install it independently:
```bash
npm install @tamagui/visually-hidden
```
## Usage
Simply wrap the content you want hidden in VisuallyHidden:
```tsx
import { Text, VisuallyHidden } from 'tamagui'
export default () => (
Add annotations here
)
```
When using with the `space` property, it will avoid double-spacing:
```tsx
import { H1, Text, VisuallyHidden, YStack } from 'tamagui'
export default () => (
Title
Add annotations here
)
```
---
# Source: https://tamagui.dev/docs/intro/why-a-compiler.md
---
title: The Frontend Trilemma
---
If you're developing a cross-platform app, you've committed to the **frontend
development trilemma**, a choose-two-of-three best shown as a diagram:
React Native recommends writing things twice generally for the best UX as is made clear in the `` of the homepage: "[learn once, write anywhere](https://reactnative.dev/)". This is opposed to the sort of holy grail mantra of "write once, runs everywhere". Doing this results in native-feeling and performing apps, while still saving quite a bit of development time thanks to sharing everything besides your views: utils, state and data management, hooks, etc.
The center of this above Venn diagram would be a sort of platonic ideal: "write once, runs _well_ everywhere". We're pretty far from it as of now, but it's not _technically_ impossible. We can imagine a few ways to get closer:
- A sort of Rails-for-React - unified routing, patterns, code gen, "all the batteries".
- A UI kit that adapts to each platform's primitives confidently with flexible APIs.
- A way to author styles that output to platform primitives without overhead: eg, CSS with media queries, pseudos, and variables on the web.
This document goes over how we can achieve the last one. The first one is doable (and Expo is [working on as much](https://expo.github.io/router/docs/)), and the second one [Tamagui UI](/docs/components/stacks) is working towards slowly.
**The idea is to make another "bump" towards properly native-experience apps with shared code, much like how React Native Web made one:**
This can be done especially on the web by reducing JS bundle size by a large % and greatly increasing render performance with reduced tree depth, logic, objects, and hooks. The Tamagui Compiler does this using partial evaluation, static extraction and hoisting, code elimination, and tree-flattening.
You can [skip to the technical details without the backstory](#how-tamagui-helps) from here if you'd like.
Universal apps (those you "write once" that "run everywhere") make sense today for many cases: side-projecting, SEO-insensitive or enterprise-only apps, people who want to ship fast, experiment more, are pre-product-market fit, or generally have apps with simpler UI. Twitter and Tinder are two larger examples of this.
But today, at best, we use hooks for media queries and themes, which basically touch every component. This causes whole-tree re-renders and more expensive main-thread time in critical areas on the web. Combine that with the CSS-in-JS approach of React Native Web greatly increasing bundle size, and even medium-complexity pages will drop from 100% Lighthouse to half or worse (our homepage, a good complex example due to showing off many features that are well-optimized, goes from 95 or so down to 80ish with the compiler off).
With all your media queries, interactive styles, themes, animations, and dynamic styles in JS, it's hard to make ambitious apps that don't feel janky.
### How Tamagui Helps
`@tamagui/static` is an optimizing compiler for React Native with four main features:
The output is smaller bundles, better runtime performance, and many more native primitives used on the web.
Here's what it does, in code:
See [more examples on the homepage](/).
Notice that the compiler turned the `Text` into a `p`, and the `YStack` into a `div` (on native, this would be `Text` and `View`). This is known as tree-flattening, and for both web and native it yields very nice improvements to render performance.
This is a typical performance improvement, where much of the gains come from flattening:
Across a few apps, we've seen 30-50% of components typically flatten, with a higher percent achievable just by being aware of how the flattening optimizes (adding the `// debug` comment to the top of the file will show a fuller output).
Meanwhile, on Native, because we can't optimize to anything beyond vanilla React Native code, the gains are less. Still, the results are impressive given you now have performance within 5% of hand-optimizing React Native code, except you get a whole suite of features for free.
You can see [more Benchmarks with explanations here](/docs/intro/benchmarks).
The compiler itself deserves more detail, which we'll expand on in the blog. For now, this serves as a decent introduction.
Compilers can dramatically improve code sharing without the typical sacrifice of performance. They don't solve every problem of universal apps, but by making responsive styling, themes and interactive styles all perform at native levels, they unlock sharing a much larger percentage of the components located in the middle to bottom of the render tree in apps.
It gives us a new choice, "Universal + Compiler" that lets us ship fast while still feeling native: