One Logo Pool Ball

System

Tamagui and One

Setting up Tamagui on One

Tamagui and One go together so well you'd swear they were made by the same person. When targeting both native and web, and wanting to share UI, you don't have many options.

Tamagui does many things that no other universal libraries do: all of it's features work with SSR without flashing of styles, it supports spring and CSS animations (also SSR safe), it supports light and dark mode (also SSR safe), it's fully type safe, it supports container queries, and it outputs nearly ideal code for each platform.

We just got this guide up, it's based on a few existing apps but we haven't tested it e2e yet. Figure it's better than nothing. If you want a real working example to look at, see the Tamagui example via npx one, or the source code for this website.

We're going to support SSR-safe light and dark mode with @vxrn/color-scheme:

npm install tamagui @tamagui/config @tamagui/vite-plugin @vxrn/color-scheme

Be sure to check out the Dark mode guide for more detail there.

Next, update your root _layout.tsx:

app/_layout.tsx

import { TamaguiProvider, Theme } from 'tamagui'
import { MetaTheme, SchemeProvider, useColorScheme } from '@vxrn/color-scheme'
import { LoadProgressBar, Slot, usePathname } from 'one'
import config from '../config/tamagui.config'
export default function Layout() {
return (
<SchemeProvider>
<ThemeProvider>
<Slot />
</ThemeProvider>
</SchemeProvider>
)
}
// this is in its own component so it can access the useColorScheme hook
const ThemeProvider = ({ children }) => {
const [scheme] = useColorScheme()
return (
<TamaguiProvider config={config} defaultTheme={scheme}>
{children}
</TamaguiProvider>
)
}

And add your config/tamagui.config.ts:

config/tamagui.config.ts

import { config } from '@tamagui/config/v3'
import { createTamagui } from 'tamagui'
export const config = createTamagui(config)

The reason we separate this file out is that it lets the Tamagui compiler optimize things. You don't have to do this, but it's pretty easy.

This preset config we recommend as it lets you just get working on your app, but if you want a custom configuration, the npx one starter has a nice custom configuration example.

Next for the Vite plugin. You don't need to set up the Tamagui Vite plugin, but even if not using the optimizing compiler, it adds a lot of nice stuff: debug attributes, broader react-native compatibility, and the optimizing compiler.

vite.config.ts

export default {
plugins: [
tamaguiPlugin({
optimize: true,
components: ['tamagui'],
config: './config/tamagui.config.ts',
})
]
}

Optimizing further

If you want to optimize things a bit further, we can output the Tamagui design system CSS to a stylesheet by changing our plugin configuration:

vite.config.ts

export default {
plugins: [
tamaguiPlugin({
optimize: true,
components: ['tamagui'],
config: './config/tamagui.config.ts',
outputCSS: './public/tamagui.css'
})
]
}

And then changing our root layout to add disableInjectCSS, and instead importing the CSS at the top:

app/_layout.tsx

import '../public/tamagui.css'
import { TamaguiProvider, Theme } from 'tamagui'
import { MetaTheme, SchemeProvider, useColorScheme } from '@vxrn/color-scheme'
import { LoadProgressBar, Slot, usePathname } from 'one'
import config from '../config/tamagui.config'
export default function Layout() {
return (
<SchemeProvider>
<ThemeProvider>
<Slot />
</ThemeProvider>
</SchemeProvider>
)
}
const ThemeProvider = ({ children }) => {
const [scheme] = useColorScheme()
return (
<TamaguiProvider config={config} defaultTheme={scheme} disableInjectCSS>
{children}
</TamaguiProvider>
)
}

That should be about it! You now have SSR safe styling, light and dark mode, and a bunch of advanced styling features.

Check out the Tamagui site for a lot documentation.

Edit this page on GitHub.