One Logo Pool Ball
wwwwwwwwwwwwwwwwwww

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 its 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.

Try concrete Tamagui examples by running npx one.

We’re going to support SSR-safe light and dark mode with the @vxrn/color-scheme helpers that one re-exports:

npm add tamagui @tamagui/config @tamagui/vite-plugin

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

Add your config/tamagui.config.ts:

config/tamagui.config.ts
import { defaultConfig } from '@tamagui/config/v5'
import { animations } from '@tamagui/config/v5-css'
import { createTamagui } from 'tamagui'
export const config = createTamagui({
...defaultConfig,
animations,
})
type Conf = typeof config
declare module 'tamagui' {
interface TamaguiCustomConfig extends Conf {}
}
export default 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. The declare module block types your tokens and themes across the app.

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

Next, update your root _layout.tsx. one re-exports the @vxrn/color-scheme helpers, so SchemeProvider and useUserScheme come straight from one:

app/_layout.tsx
import type { ReactNode } from 'react'
import { SchemeProvider, Slot, useUserScheme } from 'one'
import { TamaguiProvider } from 'tamagui'
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 useUserScheme hook
const ThemeProvider = ({ children }: { children: ReactNode }) => {
const userScheme = useUserScheme()
return (
<TamaguiProvider config={config} defaultTheme={userScheme.value}>
{children}
</TamaguiProvider>
)
}

Next for the Vite plugin. You don’t need to set up the Tamagui Vite plugin, but it adds a lot of nice stuff: debug attributes, broader react-native compatibility, and the optimizing compiler. The compiler runs for every component listed in components; pass disableExtraction: true to turn it off.

vite.config.ts
import { tamaguiPlugin } from '@tamagui/vite-plugin'
import { one } from 'one/vite'
import type { UserConfig } from 'vite'
export default {
plugins: [
one(),
tamaguiPlugin({
components: ['tamagui'],
config: './config/tamagui.config.ts',
}),
],
} satisfies UserConfig

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
import { tamaguiPlugin } from '@tamagui/vite-plugin'
import { one } from 'one/vite'
import type { UserConfig } from 'vite'
export default {
plugins: [
one(),
tamaguiPlugin({
components: ['tamagui'],
config: './config/tamagui.config.ts',
outputCSS: './public/tamagui.css',
}),
],
} satisfies UserConfig

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 type { ReactNode } from 'react'
import { SchemeProvider, Slot, useUserScheme } from 'one'
import { TamaguiProvider } from 'tamagui'
import config from '../config/tamagui.config'
export default function Layout() {
return (
<SchemeProvider>
<ThemeProvider>
<Slot />
</ThemeProvider>
</SchemeProvider>
)
}
const ThemeProvider = ({ children }: { children: ReactNode }) => {
const userScheme = useUserScheme()
return (
<TamaguiProvider config={config} defaultTheme={userScheme.value} 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 more documentation.

Edit this page on GitHub.