Next.js Guide

How to set up Tamagui with Next.js

We're putting together a better guide soon!

If you'd like to get a good idea of a set up with Next.js both this site and the create-tamagui-app default template use it. You can bootstrap a complete starter app now with:

npm create tamagui-app

Check out the source for this site  to see a good example of a full featured Next.js website, especially the next.config.js and tamagui.config.ts.

_document.tsx

You'll want to set up a _document.tsx and gather both the react-native-web style object using AppRegistry, as well as Tamagui styles using Tamagui.getCSS() into the head element:

import NextDocument, { Head, Html, Main, NextScript } from 'next/document'
import { Children } from 'react'
import { AppRegistry } from 'react-native'
import Tamagui from '../tamagui.config'
export default class Document extends NextDocument {
static async getInitialProps({ renderPage }) {
AppRegistry.registerComponent('Main', () => Main)
const page = await renderPage()
// @ts-ignore
const { getStyleElement } = AppRegistry.getApplication('Main')
const styles = [
getStyleElement(),
<style dangerouslySetInnerHTML={{ __html: Tamagui.getCSS() }} />,
]
return { ...page, styles: Children.toArray(styles) }
}
render() {
return (
<Html>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}

Themes

We've created a package that works with Tamagui to properly support SSR light/dark themes that also respect user system preference, called @tamagui/next-theme. It assumes your light/dark themes are named as such, but you can override it. This is pre-configured in the create-tamagui-app starter.

We recommend memo-ing children so they don't re-render. Here's how you'd set up your _app.tsx:

import { NextThemeProvider, useRootTheme } from '@tamagui/next-theme'
import { AppProps } from 'next/app'
import Head from 'next/head'
import React, { useMemo, useState } from 'react'
import { TamaguiProvider } from 'tamagui'
import config from '../tamagui.config'
export default function App({ Component, pageProps }: AppProps) {
const [theme, setTheme] = useRootTheme()
// memo to avoid re-render on dark/light change
const contents = useMemo(() => {
return <Component {...pageProps} />
}, [pageProps])
// because we do our custom getCSS() above, we disableInjectCSS here
return (
<>
<Head>{/* ... */}</Head>
<NextThemeProvider onChangeTheme={setTheme}>
<TamaguiProvider config={config} disableInjectCSS disableRootThemeClass defaultTheme={theme} >
{contents}
</TamaguiProvider>
</NextThemeProvider>
</>
)
}

If you need to access the current theme, say for a toggle button, you will then use the useTheme hook. We'll release an update in the future that makes this automatically work better with Tamagui's built-in useTheme.

import { useTheme } from '@tamagui/next-theme'
export default () => {
const { theme, toggleTheme } = useTheme()
return <Button>Change theme (currently: {theme})</Button>
}