wwwwwwwwwwwwwwwwwww

Configuration

Custom themes, tokens, shorthands, and media queries.

Create a tamagui.config.ts in the root of your app to set up your design system. It should only be imported by the top-level file that runs React.render, to ensure fast refresh works.

Quick Start

You can use @tamagui/config a totally complete config out of the box, the same one this site uses.

Show more
import { config } from '@tamagui/config'
import { Text, View } from 'react-native'
import { createTamagui, setupReactNative } from 'tamagui' // or '@tamagui/core'
// if using only @tamagui/core with `react-native` components
// if using `tamagui` this isn't necessary as it does this setup for you (for most components)
setupReactNative({
Text,
View,
})
const appConfig = createTamagui(config)
export type AppConfig = typeof appConfig
declare module 'tamagui' {
// or '@tamagui/core'
// overrides TamaguiCustomConfig so your custom types
// work everywhere you import `tamagui`
interface TamaguiCustomConfig extends AppConfig {}
}
export default appConfig

The source  and type output  of that config is also useful as a reference for building your own custom config.

wwwwwwwwwwwwwwwwwww

Another quick start that gives a bit more customization is using @tamagui/shorthands for our preset shorthands, and @tamagui/themes for the same themes and tokens used for this site. Then you'll just want to bring along any fonts, media queries, and animations you need.

import { shorthands } from '@tamagui/shorthands'
import { themes, tokens } from '@tamagui/themes'
import { createTamagui } from 'tamagui' // or '@tamagui/core'
const appConfig = createTamagui({
themes,
tokens,
shorthands,
})
export type AppConfig = typeof appConfig
declare module '@tamagui/core' {
interface TamaguiCustomConfig extends AppConfig {}
}
export default appConfig

Depending on if you're using just @tamagui/core, tamagui, or your own custom design library, be sure the import of createTamagui and declare module type matches to the one you're using.

Customizing themes

A lot of people want to use the Tamagui themes as the base, but then customize specific pieces. For this, we recommend literally installing @tamagui/create-theme and then copy-pasting the source of @tamagui/themes somewhere near your tamagui.config.ts .

The file uses our create-theme package which isn't documented yet but has somewhat simple source you can check out and it gives you a really complete example of building out a suite of themes, sub-themes, and component themes that you can fork or change to your taste.

We highly recommend you don't get bogged down in customizing themes too early. It's better to get used to Tamagui with the quick start, get a feel for how themes work, and then go in and customize.

Overview

Let's start with an example of a complete tamagui.config.ts which is in the code block below. Note that keys used here can be customized to your liking, but using them in combination with shorthands give you a nice syntax like this:

<XStack bg="$red" w="$2" h="$1" />
Show more
import { createMedia } from '@tamagui/react-native-media-driver'
import { createFont, createTamagui, createTokens } from 'tamagui' // or '@tamagui/core'
const interFont = createFont({
family: 'Inter, Helvetica, Arial, sans-serif',
// keys used for the objects you pass to `size`, `lineHeight`, `weight`
// and `letterSpacing` should be consistent. The `createFont` function
// will fill-in any missing values if `lineHeight`, `weight` or `letterSpacing`
// are subsets of `size`
size: {
1: 12,
2: 14,
3: 15,
},
lineHeight: {
// 1 will be 22
2: 22,
},
weight: {
1: '300',
// 2 will be 300
3: '600',
},
letterSpacing: {
1: 0,
2: -1,
// 3 will be -1
},
// (native) swap out fonts by face/style
face: {
300: { normal: 'InterLight', italic: 'InterItalic' },
600: { normal: 'InterBold' },
},
})
// these keys can be different, but again we highly recommend consistency
const size = {
0: 0,
1: 5,
2: 10,
// ....
}
export const tokens = createTokens({
size,
space: { ...size, '-1': -5, '-2': -10 },
radius: { 0: 0, 1: 3 },
zIndex: { 0: 0, 1: 100, 2: 200 },
color: {
white: '#fff',
black: '#000',
},
})
const config = createTamagui({
fonts: {
// for tamagui, heading and body are assumed
heading: interFont,
body: interFont,
},
tokens,
themes: {
light: {
bg: '#f2f2f2',
color: tokens.color.black,
},
dark: {
bg: '#111',
color: tokens.color.white,
},
},
// `@tamagui/core` doesn't provide media query capabilities out of the box
// for native as it is de-coupled from react-native.
// For web-only, media queries work out of the box and you can avoid the
// `createMedia` call here by passing the media object directly.
// If targeting React Native, add this driver and `createMedia` call.
// `tamagui` and `config` do this for you automatically.
media: createMedia({
sm: { maxWidth: 860 },
gtSm: { minWidth: 860 + 1 },
short: { maxHeight: 820 },
hoverNone: { hover: 'none' },
pointerCoarse: { pointer: 'coarse' },
}),
// optional:
// add custom shorthand props
// note: as const is important, without it you may see breaking types
shorthands: {
px: 'paddingHorizontal',
f: 'flex',
w: 'width',
} as const,
// Experimental / advanced, only for overriding the core component styles
// Prefer to use styled() for building your own, only useful for edge cases.
defaultProps: {
Text: {
// override any default props here
},
},
})
type AppConfig = typeof config
// this will give you types for your components
// note - if using your own design system, put the package name here instead of tamagui
declare module 'tamagui' {
interface TamaguiCustomConfig extends AppConfig {}
}
export default config

The createTamagui function receives a configuration object:

  • tokens: Use createTokens to generate variables in your theme and app.
  • theme: Define your design theme, which map to CSS properties.
  • media: Define reusable responsive media queries.
  • shorthands: Define any props you want to expand to style values, keys being the shorthand and values being the expanded style prop.
  • defaultProps: For more advanced uses, you can override all named styled() components initial values. These merge downwards, so styled(Text, { name: 'Paragraph' }) will get any defaultProps set for Text.
  • onlyAllowShorthands when set to true will ensure that if you define shorthands, they override existing long-form style props.

Note, for tamagui (not core), it expects you to define a true token that maps to your default size, this way it knows what token to use by default. So you'd do something like this:

export const tokens = createTokens({
size: {
small: 20,
medium: 30,
true: 30, // note true = 30 just like medium, your default size token
large: 40,
},
space: {
small: 10,
medium: 20,
true: 20, // same goes for space and other token categories
large: 30,
},
})

The compiler will parse your tamagui.config.ts file at build-time. For this reason, we recommend keeping it relatively simple. Don't import heavy dependencies, and prefer to import createTamagui and other helpers directly from @tamagui/core.

Add Provider

Import and use the Tamagui Provider component at the top component in your app.

import { TamaguiProvider } from 'tamagui'
import config from './tamagui.config'
export default function App() {
return (
<TamaguiProvider config={config}>
<AppContents />
</TamaguiProvider>
)
}

Using Tamagui Provider

TamaguiProvider takes a few optional properties:

Props

  • defaultTheme (required)

    string

    The initial top level theme.

  • disableRootThemeClass

    boolean

    Disable inserting a theme class in the DOM or context, allowing you to manually place it higher. For custom use cases like integration with next-theme.

  • disableInjectCSS

    boolean

    By default Tamagui inserts CSS with a useLayoutEffect on load. But if you're setting up SSR you'll want to use getCSS() on the server instead and then turn on this setting.

  • If you use disableInjectCSS, you'll need to use this somewhere to insert your CSS into head:

    import Tamagui from './tamagui.config'
    const css = Tamagui.getCSS()
    // insert into your head

    Note that you can optimize this a bit by generating your CSS at build-time and then avoiding generating it on the client. This is covered in the Next.js Guide as it gives a good example of how to put all the pieces together properly, but you can apply it to any other framework.

    To do this you would do something like this:

    Tamagui.getCSS({
    // if you are using "outputCSS" option, you should use this "exclude"
    // if not, then you can leave the option out
    exclude: process.env.NODE_ENV === 'production' ? 'design-system' : null,
    })

    And then you'll want to set outputCSS as documented in the Compiler docs and import the CSS that the Tamagui compiler emits somewhere from your app.

    See the Next.js guide for further documentation on how to set it up.

    Make sure the style tag you insert with the getCSS styles is inserted after any react-native-web style tags to ensure Tamagui can override the built-in styles.

    Including the reset css

    There is an optional CSS reset that helps normalize styling, you can import it into your app like so:

    import '@tamagui/core/reset.css'

    Setup media query driver

    @tamagui/core doesn't provide media capabilities out of the box to native apps, while on the web it uses native media queries. To enable media queries for React Native, you need to provide matchMedia implementation:

    yarn add @tamagui/react-native-media-driver
    import { createMedia } from '@tamagui/react-native-media-driver'
    export default createTamagui({
    media: createMedia({
    xs: { maxWidth: 660 },
    // ...
    }),
    })

    Tokens

    Tokens have a structure inspired by but divergent from the Theme UI spec . They are mapped to CSS variables at build time.

    Font tokens

    The font tokens are a bit special and are created with createFont:

    Show more
    const interFont = createFont({
    family: 'Inter, Helvetica, Arial, sans-serif',
    size: {
    1: 12,
    2: 14,
    3: 15,
    // ...
    },
    lineHeight: {
    1: 17,
    2: 22,
    3: 25,
    // ...
    },
    weight: {
    4: '300',
    6: '600',
    },
    letterSpacing: {
    4: 0,
    8: -1,
    },
    // for native only, alternate family based on weight/style
    face: {
    // pass in weights as keys
    700: { normal: 'InterBold', italic: 'InterBold-Italic' },
    800: { normal: 'InterBold', italic: 'InterBold-Italic' },
    900: { normal: 'InterBold', italic: 'InterBold-Italic' },
    },
    })

    Note, you don't need to use numbered keys, you can use sm or tiny or whatever you'd like. But you do want keep those keys consistent.

    This gives you a lot of power over customizing every aspect of your design based on each font family. In other styling libraries that follow the Theme UI spec, you generally don't group your size/lineHeight/weight/etc tokens by the family, which means you are forced to choose a single vertical rhythm no matter the font.

    Things to note:

    • The keys of size, lineHeight, weight, and letterSpacing are meant to match.
    • Define the full set of keys on size, the rest can be a subset.
    • Missing keys from partially defined objects will be filled in.
      • In the example above, weight is only defined at 4 and 6.
      • At creation Tamagui fills in the missing keys with previous value, or the next one if no previous exists. So weight 1 === 300, weight 5 === 300, and weight 7 === 600.
    wwwwwwwwwwwwwwwwwww

    Non-font tokens

    The rest of the tokens categories besides font are flatter. The space and size generally share keys, and that space can generally use negative keys as well.

    // passed into createTamagui
    const tokens = createTokens({
    color: {
    white: '#fff',
    black: '#000',
    },
    })

    You access tokens then by using $ prefixes in your values. Tamagui knows which tokens to use based on the style property you use.

    const App = () => (
    <Text fontSize="$lg" lineHeight="$lg" fontFamily="$mono" color="$white">
    Hello world
    </Text>
    )

    One final note: using tokens with themes. Tokens are considered a "fallback" to themes, so any values you define in your theme will override the token. The next section will explain this further.

    Configuring tokens

    You have a few options on the configuration object that control how strict your style values are allowed to be.

    • ``
    • ``

    Themes

    Themes live one level below tokens. Tokens are your variables, where themes use those tokens to create consistent, generic properties that you then typically use in shareable components. Themes should generally only deal with colors.

    Tamagui components in general expect a set of theme keys to be defined like the following, but you can deviate if you create your own design system.

    const light = {
    background: '#fff',
    backgroundHover: tokens.color.gray2,
    backgroundPress: tokens.color.gray4,
    backgroundFocus: tokens.color.gray5,
    backgroundTransparent: tokens.color.grayA1,
    borderColor: tokens.color.gray4,
    borderColorHover: tokens.color.gray6,
    borderColorPress: tokens.color.gray12,
    borderColorFocus: tokens.color.gray11,
    color: tokens.color.gray10,
    colorHover: tokens.color.gray9,
    colorPress: tokens.color.gray8,
    colorFocus: tokens.color.gray8,
    shadowColor: tokens.color.grayA4,
    shadowColorHover: tokens.color.grayA6,
    shadowColorPress: tokens.color.grayA8,
    shadowColorFocus: tokens.color.grayA8,
    ...lightColors,
    }

    You don't have to use tokens as your theme values, but if you do they avoid some overhead. With Tamagui, the idea is that bg, color, and borderColor represent the "primary" and clearest colors, and bg2, color2 etc get more subtle.

    To see how it works, here's a snippet from InteractiveFrame which is the frame component that's used in Button:

    export const InteractiveFrame = styled(XStack, {
    borderRadius: '$1',
    paddingVertical: '$2',
    paddingHorizontal: '$3',
    backgroundColor: '$background',
    justifyContent: 'center',
    alignItems: 'center',
    cursor: 'pointer',
    flexWrap: 'nowrap',
    flexDirection: 'row',
    flexShrink: 1,
    hoverStyle: {
    backgroundColor: '$backgroundHover',
    },
    pressStyle: {
    backgroundColor: '$backgroundPress',
    },
    // ...
    })

    Media

    For more full docs on media queries, see the useMedia docs page.

    Animations

    Choose one of @tamagui/animations-css, @tamagui/animations-react-native or @tamagui/animations-reanimated. Note that reanimated support is not as well supported.

    Append a new key animations to your createTamagui call:

    import { createAnimations } from '@tamagui/animations-react-native'
    // pass this exported `animations` to your `createTamagui` call:
    export const animations = createAnimations({
    bouncy: {
    damping: 9,
    mass: 0.9,
    stiffness: 150,
    },
    lazy: {
    damping: 18,
    stiffness: 50,
    },
    }),
    })

    If you want to have different drivers for each platform, like a CSS driver for the web and react-native for native, simply use the React Native convention of having platform-specific files: add a animations.ts and animations.native.ts. Set them up just like above, with matching keys, but importing from their respective driver.

    Shorthands

    Shorthands are defined directly on your createTamagui call, rather than being attached to each component, to de-couple shorthands from components. This forces component kits to standardize on the basic react-native style syntax and ensures there's only one set of abbreviations defined ever.

    Here's an example of a partial shorthands configuration:

    // the as const ensures types work with the optional `onlyAllowShorthands` option
    const shorthands = {
    ac: 'alignContent',
    ai: 'alignItems',
    als: 'alignSelf',
    bblr: 'borderBottomLeftRadius',
    bbrr: 'borderBottomRightRadius',
    bc: 'backgroundColor',
    br: 'borderRadius',
    btlr: 'borderTopLeftRadius',
    btrr: 'borderTopRightRadius',
    f: 'flex',
    // ...
    } as const
    export default createTamagui({
    shorthands,
    })

    For a full configuration, see @tamagui/shorthands 

    Others

    Props

  • defaultFont

    Map it to the regular key of the font given to createTamgui, so "body" or "heading" make sense as valiues here. This will ensure this font is the fallback for any views that don't define it.

  • mediaQueryDefaultActive

    Record<string, boolean>

    For the first render, determines which media queries are true (useful for SSR).

  • cssStyleSeparator

    string

    What's between each generated CSS style rule. Set as newline to more easily debug outputted CSS.

  • themeClassNameOnRoot

    boolean

    When using next-themes or anything that does SSR and attaches the theme class to the HTML tag, set this to true to have the proper CSS theme selectors generate

  • shouldAddPrefersColorThemes

    boolean

    Default: 

    Generates @media queries based on prefers-color-scheme for you if you have light/dark themes.

  • maxDarkLightNesting

    number

    Default: 

    3

    (Advanced) On the web, tamagui treats "dark" and "light" themes as special and generates extra CSS to avoid having to re-render the entire page. This CSS relies on specificity hacks that multiply by your sub-themes. This prop sets the maxiumum number of nested dark/light themes you can do. Defaults to 3 for a balance, but can be higher if you nest them deeply.

  • onlyAllowShorthands

    boolean

    Will remove the type for the long-form versions of any shorthands you define.

  • selectionStyles

    (theme) => ({ backgroundColor: Variable | string; color: Variable | string })

    On the web, will generate ::selection styles for text selection.

  • settings

    TamaguiSettings

    See Settings section below.

  • Settings

    Props

  • allowedStyleValues

    AllowedStyleValuesSetting

    Set up allowed values on style props, this is only a type-level validation (see below).

  • autocompleteSpecificTokens

    boolean | 'except-special'

    Set up if "specific tokens" ($color.name) are added to the types where tokens are allowed (see below).

  • AllowedStyleValuesSetting:

    Defaults to false:

    • "strict" - only allows tokens for any token-enabled properties
    • "strict-web" - same as strict but allows for web-specific tokens like auto/inherit
    • "somewhat-strict" - allow tokens or:
      • for space/size: string% or numbers
      • for radius: number
      • for zIndex: number
      • for color: named colors or rgba/hsla strings
    • "somewhat-strict-web" - same as somewhat-strict but allows for web-specific tokens
    • false (default) - allows any string (or number for styles that accept numbers)
    type AllowedValueSetting =
    | boolean
    | 'strict'
    | 'somewhat-strict'
    | 'strict-web'
    | 'somewhat-strict-web'
    type AllowedStyleValuesSetting =
    | AllowedValueSetting
    | {
    space?: AllowedValueSetting
    size?: AllowedValueSetting
    radius?: AllowedValueSetting
    zIndex?: AllowedValueSetting
    color?: AllowedValueSetting
    }

    autocompleteSpecificTokens:

    The VSCode autocomplete puts specific tokens above the regular ones, which leads to worse DX. If true this setting removes the specific token from types for the defined categories.

    If set to "except-special", specific tokens will autocomplete only if they don't normally use one of the special token groups: space, size, radius, zIndex, color.