styled()

Extend and build custom and optimizable components.

Create a new component by extending an existing one:

import { GetProps, Stack, styled } from '@tamagui/core'
export const Circle = styled(Stack, {
name: 'Circle', // useful for debugging, and Component themes
borderRadius: 100_000_000,
})
// helper to get props for any TamaguiComponent
export type CircleProps = GetProps<typeof Circle>

Usage:

<Circle x={10} y={10} backgroundColor="red" />

Note, tamagui and @tamagui/core both export many of the same helpers, like styled. If you are using tamagui, you don't need to ever add @tamagui/core to your package.json or import it and can instead import directly from tamagui itself.

The styled function only accepts Tamagui components or components that accept the className property, for now. There is a workaround option to support react-native-web components, but it's not a public API as it will likely change with their next release.

You can pass any prop that is supported by the component you are extending, even variants of the parent component. Tamagui will figure out the style props up-front, turn them into classNames, and then pass the non-style props down to the component as defaultProps.

Variants

Let's add some variants:

import { Stack, styled } from '@tamagui/core'
export const Circle = styled(Stack, {
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,
})

Please use as const for the variants definition until Typescript gains the ability to infer generics as const .

We can use these like so:

<Circle pin="top" centered size="$lg" />

To learn more about to use them and all the special types, see the docs on variants.

Advanced

If you are building a design system, you may like the .extractable static property that styled components include. It's useful for when you need a component with some logic inside of it (more than what styled variants support), but still want to support compiler extraction.

It's easier to understand through example. Let's say you want a component that always puts an icon somewhere inside of it:

import { Heart } from '@tamagui/feather-icons'
import { GetProps, YStack, styled } from 'tamagui'
// not exported
const CircleFrame = styled(YStack, {
alignItems: 'center',
justifyContent: 'center',
// ... your custom styles here
})
type CircleProps = GetProps<typeof CircleFrame>
export const CustomCircle = ({ children, ...props }: CircleProps) => {
return (
<CircleFrame {...props}>
<Heart size={20} />
{children}
</CircleFrame>
)
}

As written above, when you use CustomCircle, the Tamagui compiler won't know to extract usages of it, so this will be left entirely to run at runtime:

export default () => (
<CustomCircle x={10} y={10} scale={1.5} hoverStyle={{ x: 20, y: 20 }}>
{/* ... your contents ... */}
</CustomCircle>
)

This should work fine, but ideally you get some optimization on this. Let's make a change to the CustomCircle definition:

export const CustomCircle = CircleFrame.extractable(({ children, ...props }: CircleProps) => {
return (
<CircleFrame {...props}>
<Heart size={20} />
{children}
</CircleFrame>
)
})

Now we wrap CustomCircle with CircleFrame.extractable. Notice, as is shown above, components wrapped with extractable must return a single component, the component that extractable comes from. In this case CustomCircle must return a single CircleFrame, and spread all the props it accepts down to it.

Now, when use CustomCircle, the compiler can do something like this:

const _cn = `transform-1234 hover-transform-1234`
export default () => <CustomCircle className={_cn}>{/* ... your contents ... */}</CustomCircle>

This nets you some runtime performance and ensures any unique styles are pulled into CSS at build-time.

Compatibility

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:

import { styled } from '@tamagui/core'
import { SomeCustomComponent } from 'some-library'
export const TamaguiCustomComponent = styled(SomeCustomComponent, {
acceptsClassName: true,
})