https://tamagui.dev/blog/version-one
#
Docs
Blog
More
/
Github
Blog
Tamagui 1.0
Ship cross-platform React apps in 1/2 the time with 2x performance.
Nate Wienert
December 2022
43 min read
React Native is amazing for building mobile apps, but sharing
components between web and native sacrifices performance,
productivity and UX.
Tamagui introduces a novel optimizing compiler for React that
evaluates your code at build-time, turning heavy JS into flatter
trees and much faster CSS. The result is more code share %, less dev
time, and lighter, faster apps.
Tamagui's optimizing compiler makes advanced optimizations using
partial evaluation, CSS extraction, and ultimately tree-flattening.
Where it optimizes, things get a lot faster:
Tamagui
0.02ms
react-native-web
0.063ms
Dripsy
0.108ms
NativeBase
0.73ms
Rendering between groups of styles conditionally
Tamagui comes in three parts:
Tamagui Core (@tamagui/core) is a style library for React Native and
web that supports 100% of the React Native API surface in a
0-dependency library. It adds all the goodies of modern style
libraries into a cohesive package.
Tamagui Static (@tamagui/static) is an optimizing compiler on top of
Core, turning even inline styles sprinkled with logic into atomic CSS
and minimal JS, flattening your view tree for maximum performance.
Tamagui UI (tamagui) is a component kit built on top of the
incredibly powerful theme primitives of core. That, alongside
composable-component APIs and complete size control enables a new
level of control not seen before in a UI kit.
wwwwwwwwwwwwwwwwwww
Overview
Lets get familiar with both @tamagui/core (the styling library) and
@tamagui/static (the compiler), and how the latter optimizes the
former.
StylesLogicMediaHooks
A powerful `styled` constructor, inline props, shorthands and more.
Input
app.tsxheading.tsxtamagui.config.ts
import { Stack } from '@tamagui/core'
import { Heading } from './Heading'
const App = (props) => (
Lorem ipsum dolor.
)
Styles extracted, logic evaluated, and a flatter tree with atomic CSS
styles per-file.
Output
app.jsapp.css
import { Stack } from '@tamagui/core';
import { Heading } from './Heading';
export default (props) => (
Lorem ipsum dolor.
);
const _cn4 = " _col-b5vn3b _ff-4yewjq";
const _cn3 = " _ml-0px _mb-0px _mr-0px _mt-0px _col-b5vn3b _tt-3tb9js _ff-4yewjq _fow-3uqci0 _ls-3w5fg8 _fos-3slq2o _lh-3or5x5 _cur-text _ussel-text _ww-break-word _bxs-border-box _dsp-inline ";
const _cn2 = " is_Heading font_heading";
const _cn = " is_Stack _fd-column _miw-0px _mih-0px _pos-relative _bxs-border-box _fb-auto _dsp-flex _fs-0 _ai-stretch _w-550px _pr-1aj148u _pl-1aj148u _pr-_gtSm_1aj14ca _pl-_gtSm_1aj14ca ";
The key to Tamagui's performance gains lie in view flattening or
tree-flattening: the compiler analyzes code (even across module
boundaries) and evaluates styled components away using both the AST
and Node VM code evaluation. It will fully flatten custom styled
components (like ) into plain old (or on
native).
wwwwwwwwwwwwwwwwwww
Hey! Listen!
Tamagui is fully OSS, self-funded and built by me.
My goal is to support Tamagui development with sponsorships that get
early access to some really interesting new features.
Sponsor
wwwwwwwwwwwwwwwwwww
Overview of @tamagui/core
Core is a complete cross-platform style library. It allows creating
typed design systems that work with a styled factory on both React
Native and web:
Highlights
* Works 100% the same across Native and Web.
* Works completely at runtime, no compiler necessary.
* <17kb, 0-dependency. Every React Native API, typed, without bloat
on the web.
* SSR, React Server Component, Concurrent mode support for 100% of
the features.
* Custom typed design systems.
* Powerful typed variants, including functional variants.
* Prop-order based styling means no more CSS specificity fights!
* Inline styles with tokens, media and pseudo styles that all
optimize away.
* Typed shorthands.
* Pluggable animation drivers, allows swapping JS-based animations
to pure CSS on the web.
One novel feature is deterministic, easy to reason about style
merging. Core always merges styles based on the order received rather
than arcane CSS definition-order + specificity rules, no matter if
those styles come through nested styled calls, or via inline props.
This resolves limitations in both CSS and CSS-in-JS systems as they
largely exist today.
styled()
Here's an example of the styled function with a few variants:
import { Stack, styled } from '@tamagui/core'
export const Circle = styled(Stack, {
// access your tokens and theme values easily with $ props
backgroundColor: '$background',
borderRadius: '$4',
// media and pseudo styles - this would take 15+ lines of brittle JS in RN
$gtSm: {
pressStyle: {
borderRadius: '$6',
},
},
variants: {
// define variants
// these will flatten, even when nesting multiple styled() calls
pin: {
top: {
position: 'absolute',
top: 0,
},
},
size: {
// functional variants give incredible power and save bundle size
'...size': (size, { tokens }) => {
return {
width: tokens.size[size] ?? size,
height: tokens.size[size] ?? size,
}
},
},
} as const,
})
For more, see the configuration, styling, and variants docs.
Design system
Using createTamagui you generate a fully-typed design system with
colors, tokens, spacing, sizing, fonts, themes, shorthands,
animations, and more.
It works with well-optimized and easy to use hooks like useMedia and
useTheme that save a ton of time over rolling these yourself.
Here's a stripped down configuration:
import { createFont, createTamagui, createTokens } from '@tamagui/core'
const interFont = createFont({
family: 'Inter, Helvetica, Arial, sans-serif',
size: { 1: 12, 2: 14 /* ... */ },
// ... lineHeight, weight, letterSpacing, transform, style, color, face
})
const size = { 0: 0, 1: 5, 2: 10 /* ... */ }
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' },
})
export default createTamagui({
fonts: {
heading: interFont,
body: interFont,
},
tokens,
themes: {
light: { bg: '#f2f2f2', color: tokens.color.black },
dark: { bg: '#111', color: tokens.color.white },
},
media: {
sm: { maxWidth: 860 },
gtSm: { minWidth: 860 + 1 },
},
shorthands: {
px: 'paddingHorizontal',
f: 'flex',
},
})
Stack and Text
Finally, the Stack and Text components work with styled() and provide
lot of nice features like hover, press, and focus styles, typed
inline style props with theme and token values, shorthands, media
queries, animations, and more.
wwwwwwwwwwwwwwwwwww
What's new in @tamagui/core
Over a thousand bugfixes since beta with large performance and memory
improvements, much higher test coverage, and compatibility with the
latest and greatest in React and React Native.
Compatibility
Across all of Tamagui, we've bumped support to the latest versions.
Due to improvements in react-native-web 18, we were also able to
dramatically simplify setup, no longer having to patch the library to
support classnames.
Tamagui supports React Native 0.70, React 18, React Native Web 18.
It's the only Native UI library that fully supports concurrent mode
on the web as well.
Big gains for web-only use cases
Early in the beta for web-only use cases, Core had the downside of
requiring you to bundle all of react-native-web in your app, not to
mention the always tricky problem of configuring your bundler to
alias react-native to react-native-web.
For 1.0, we've made the difficult steps to get @tamagui/core to have
no dependency at all on react-native (and therefore no need to setup
anything in your bundler), while still maintaining 100% compatibility
with the React Native APIs.
This unlocks a few things:
* Much easier setup: no need to touch your bundler.
* Save ~30Kb of bundle size by avoiding react-native-web entirely
for web-only use cases.
You can get all the benefits of unified APIs across native and web
plus an optimizing compiler and full-featured design system, yet
actually save bundle size vs React Native Web.
Bundle size reduction
De-coupling core from react-native is a big win for web bundle size,
allowing you to leave out 35Kb of size if just using core. We've
shaved nearly ~12Kb beyond that during the betas by iteratively
improving code and reducing dependencies. Today core is ~20Kb,
gzipped, but can get down to ~10Kb fairly straightforwardly.
Beyond that, we have landed refactors now that allow releasing a
web-specific core which will drop a few Native-specific APIs and
bring the total runtime size cost down to below 8Kb.
Performance improvements
We've greatly optimized render performance during the beta across the
board. Some of the more interesting improvements are:
useTheme
The useTheme hook is used by every Tamagui component, and is smart
about re-renders by tracking which keys are accessed by your styles
to only re-render when their values change. But it wasn't as smart
about memory usage and avoiding work up front, and was also
re-parenting the React tree too often.
We saw 8% improvements in Lighthouse scores on the website homepage
just by optimizing this.
useMedia
Supporting SSR often means you need to hydrate using an initial
value, and then re-render after hydration with the "real" media query
value. We've moved over to use React's useSyncExternalStore hook ,
which greatly reduced code and helps avoid re-renders and hooks used
per-component. It also clears us to remove the double-render that SSR
based apps typically do on hydration.
Avoiding work generating styles
Previously, Core used react-native-web to handle the final steps of
taking styles and converting them into CSS. In profiling and reading
over the code, we found this to be a large bottleneck to performance
as it would iterate over objects many times, and generate many
intermediate objects in the process.
@tamagui/core is now entirely dependency-free and generates it's own
styles. In total, de-coupling saved us 4-5 loops over the generated
style objects per-component.
styled upgrades
The styled factory has undergone a number of improvements. It now
supports wrapping any component you give it, so long as that
component accepts a style prop.
A note on styled types
We now recommend using as const after your variants object definition
to fix some tricky issues related to some outstanding Typescript
limitations around inferring const generics.
Fonts
Supporting fonts per-language is now possible with ,
like so. First, you can specify language-specific fonts with a suffix
in your configuration, much like a sub-theme:
const bodyFontEn = createFont({
family: '"Helvetica"',
// ... per-font design tokens
})
const bodyFontMandarin = createFont({
family: '"Helvetica Mandarin"',
// ... per-font design tokens
})
export const config = createTamagui({
fonts: {
body: bodyFontEn,
body_mandarin: bodyFontMandarin,
},
})
Then you can change the family to Mandarin at any point in your React
tree, with types working as expected:
import { FontLanguage, Text } from '@tamagui/core'
export default () => (
{/* TODO hello world in mandarin */}
)
config.fonts.[fontname].face
React Native makes loading fonts a bit trickier than the web, and the
easiest way to do it involved naming your font family differently
per-weight. On the web you'd have Helvetica and just change
font-weight, but on Native you set the family from Helvetica to
Helvetica Bold instead of the weight.
Tamagui added support for this through the face option on font
configurations.
The themeShallow prop
The theme prop in Tamagui by default will re-theme all sub-component.
As of beta TODO, Tamagui supports the boolean themeShallow prop on
any Tamagui component, which will only theme that exact component,
leaving all children components with the same theme as their parent.
Helpers
The loadTheme and updateTheme helpers
Themes load in a large variety of tokens for spacing, sizing, radius,
colors, and more. They are incredibly powerful, but they also have
some cost in bundle size.
The loadTheme utility function means you can only load the default
themes you want to serve, saving on bundle size, and then add in
alternative themes later on.
Meanwhile updateTheme gives you the ability to dynamically modify
themes on the fly, changing any of their values. On the web, themes
work through CSS variables, meaning updateTheme is incredibly fast as
it avoids all React re-rendering if no theme values are currently
being relied on for dynamic styles.
The useMediaPropsActive hook
This hook is useful for authoring your own custom components built on
Tamagui. The Tamagui UI kit makes use of this hook extensively.
Tamagui has a philosophy that everything Just Works(tm)[?] at runtime as
well as compile-time. The compile-time side just makes it all a lot
faster. By working fully at runtime, it means you have more
flexiblity (no need to use any plugins at all), but also lots of
power. For example, if Tamagui didn't work fully runtime, you
couldn't support animations with your design system at all.
The useMediaPropsActive is an important part of that functionality,
making it easy to properly access the "currently active" set of
styles given the current screen size. Here's an example:
import { Stack, StackProps, useMediaPropsActive } from '@tamagui/core'
const CustomWidget = (props: StackProps) => {
const activeProps = useMediaPropsActive(props)
console.log(`The current color for this screen size is`, activeProps.backgroundColor)
return
}
export default () => (
)
wwwwwwwwwwwwwwwwwww
Overview of @tamagui/static
Static is a babel and node-based optimizing compiler, specifically
for frontend React code. It supports web and native through a plugin
interface that has adapters for Webpack, Vite, Next, React Native,
and Expo. Over a dozen major improvements to the compiler have made
their way in since beta. Some of the highlights are:
On the web, render performance is much improved, especially for
interactive (pseudo) and responsive (media query) styles. It improves
Lighthouse scores 10-25%.
For more information on how it works and why it's important, read our
introduction to the compiler article.
wwwwwwwwwwwwwwwwwww
What's new in @tamagui/static
Performance
The compiler uses babel to parse and optimize components, which it
can get away with for performance largely because it only needs to
parse a subset of your files, and it only needs to look for a few
areas - namely JSX elements and styled() functions.
During the beta we landed many performance improvements bringing
average parse time down to below 6ms for a longer file on a modern
laptop. By default now Tamagui avoids parsing many more files through
quick heuristics. It's now also fully thread-safe, uses more caching,
and has been fine-tuned for memory usage.
Coverage
Previously, Tamagui Static only knew how to optimize components found
in your separated design system package. But it's both common and
desirable to have one-off styled() definitions that are just used for
small areas of your app that live alongside those areas, outside your
design system.
The compiler now supports analyzing components outside of just your
design system, allowing for even less friction when writing apps. It
means you can put your styled() definitions anywhere without
worrying, and Tamagui Static will load and optimize those components
as it discovers them.
Vite support
Tamagui now fully works with Vite 3 and 4, both with the compiler and
without. A simple vite.config.js would look like this:
import { tamaguiExtractPlugin, tamaguiPlugin } from '@tamagui/vite-plugin'
import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite'
const tamaguiCompilerConfig = {
components: ['tamagui'],
config: 'tamagui.config.ts',
useReactNativeWebLite: true,
}
export default defineConfig({
clearScreen: false,
plugins: [
react({
fastRefresh: true,
jsxPure: true,
}),
tamaguiPlugin(tamaguiCompilerConfig),
tamaguiExtractPlugin(tamaguiCompilerConfig),
],
})
Correctness
Most of the work during beta focused on correctness. We've increased
testing to cover many more areas, and landed a number of important
correctness fixes alongside the ones in core.
wwwwwwwwwwwwwwwwwww
Overview of Tamagui
Tamagui UI is a suite of React components and hooks that adapt nicely
to both Native and Web.
* Every component works on Native and Web and adapts properly to
each platform.
* Lightweight default styles included but completely customizable.
* Complete theme control - totally transform the look and feel down
to individual components without fuss, even avoiding re-renders
on theme changes (web).
* Complete size control - every component can be scaled up/down
based on your custom design system tokens.
* Composable Component APIs - enabling much more control over
styling.
* A novel Adapt component allows for completely changing UI based
on platform and media query.
wwwwwwwwwwwwwwwwwww
What's new in Tamagui
A whole lot of new components
We've seen the addition of seven new components. Each comes with
composable component API for much more complete control over each
piece of the components visuals and behavior.
We owe thanks to both Radix and Floating UI for making these much
easier to build.
Like the existing components in Tamagui, every new component is
sizable, themable, and able to be automatically styled using your
design system.
The new components are:
Sheet
OpenType: Inline
This was an excellent test of the unified animation drivers on
Tamagui, and unified interaction primitives of React Native, and
we're happy with how it's turned out.
Internally, we've come up with some helpers for each animation driver
to support a consistent API for taking a number value and
interpolating it with some style properties. Each driver handles this
quite differently on the surface, but the Tamagui drivers now give a
consistent interface across them all.
Select
Dialog
Show content in a Dialog that can adapt to another component
depending on the screen size.
Edit Profile
Show code
AlertDialog
Show a Dialog specialized for confirming or denying an action with
AlertDialog.
Show Alert
Show code
AlertDialog is the first of our components to include the native
prop. When set to true, it adapts the element to use platform native
visual containers when possible. For AlertDialog, this means that on
iOS and Android, you'll see a native Alert dialog prompt.
Slider
A draggable Slider allows users to input values within a range.
Show code
Label
Label has been updated to work with all the new form inputs.
Name[Nate Wienert ]
Notifications
[ ]
Show code
Card
Display content with a header, footer, background image, title,
subtitle and description using Card.
Sony A7IV
Now available
Purchase
[2Q]
Sony A7IV
Now available
Purchase
[2Q]
Show code
ListItem
ListItem allows you to display content with a title, subtitle, before
and after images or icons, and more.
StarTwinkles
Moon
Sun
Cloud
StarSubtitle
MoonSubtitle
Show code
Avatar
Show code
Spinner
Show code
Progress
Size: 4
Load
Show code
XGroup and YGroup
FirstSecondThird
FirstSecond
First
SecondSecond subtitle
Third
The Group component has been split into XGroup and YGroup to match
Stacks. It's been upgraded across the board with the ability to be
scrollable and to better handle passing children styles.
ScrollView
Tamagui now exports ScrollView, the exact same as React Native
ScrollView but with all Tamagui props supported.
wwwwwwwwwwwwwwwwwww
What else is new
Apps
The Kitchen Sink
The monorepo now includes apps/kitchen-sink, a native app that demos
every component that comes in Tamagui. This has helped us quickly
iterate and improve components and their correctness across Android
and iOS. Android especially received a wide variety of fixes during
the betas.
Starter repos
create-tamagui underwent some big improvements that will set it up to
be much more useful going forward. We moved starters into the
monorepo, which was tricky but lets us iterate much more rapidly on
them and test them e2e for each release. We're using a custom ~
/.tamagui home directory and git clone that gives us a much smoother
upgrade experience, especially in future versions. The
next-expo-solito starter repo has had extensive polish as well,
simplifying the setup, removing the need for watch commands, and
properly building to production across all features for web, iOS and
Android.
Features
React Native Animation Driver
We added a new animation driver, @tamagui/animations-react-native
that works with the built-in Animation API of React Native. This is
in addition to the existing @tamagui/animations-css and @tamagui/
animations-reanimated drivers. The advantages of this driver are that
you pay no extra cost for spring animations on the web when you're
already using react-native-web, and a simpler setup compared to
Reanimated.
SSR and React Server Components
During the beta we landed a wide variety of correctness fixes, tests
and runtime safeguards for SSR and landed early support for React
Server Components. We've tested them in early versions of Hydrogen
and Next.js and they should generally work when the environment
variable ENABLE_RSC is set.
Vite Plugin + Vite Compiler
We've landed Vite support. Check out the Vite guide for how to
install Tamagui with Vite.
react-native-web-lite
As a temporary measure, we released a modernized version of
react-native-web. It improves tree shaking, lands complete concurrent
support for React 18, and strips out a few deprecated areas.
Concurrent Mode Support
We've updated Tamagui's base React version to be 18, and landed a
large amount of fixes relating to concurrent mode. Every feature in
Tamagui now fully supports concurrent mode, and with
react-native-web-lite, so does every feature in the React Native API
surface for web.
@tamagui/next-theme
See the Next.js guide for more on how to use this helpful library
that automatically handles light/dark/system color scheme
preferences.
@tamagui/theme-base upgrades
The default theme package now includes _active sub-themes, paving the
way for a consistent way to style all active states across every
component in tamagui.
It also now includes theme values for color1 => color12 as part of
each theme. This gives you granular access outside of the more
specific color values like background, or borderColor.
Usage in production
Tamagui now has several apps in production in both app stores. One
larger one built by a household name company is currently under NDA,
but we can announce soon. We're proud of how active the Discord has
become with users building high quality apps!
Polish
Correctness
More than any other section in this release, the biggest amount of
effort for 1.0 went into correctness. Tamagui has steadily landed
fixes across every feature and component since Beta, and the
difference between early this year and now is astounding, with a ton
of edge cases handled automatically now.
Community
We've been building a great community, especially on Discord . Check
out the Community page for more information including diverse
community-maintained starter kits and Figma files.
Documentation
The docs have undergone continuous improvements. The compiler now has
an extensive article breaking down the whys and hows of how it works.
And all the guides have been iterated on and tested from scratch to
verify they work.
Testing
We've expanded our testing signficantly: 10x more tests, package.json
linting, full CI/CD, stricter linting in general, and a few big
integration tests.
wwwwwwwwwwwwwwwwwww
Going forward
Tamagui is stable and free of all blocking bugs across every feature,
but there's a so much more to go to make every part of it simpler,
lighter, faster, and more correct both in types and behavior.
Writing about what comes after 1.0 is a blog post to itself. For now,
next.md is a living document of upcoming features and fixes.
The next few minor releases will focus on improving types,
accessibility, animations, test coverage, and solidifying the newer
components. We'll be setting up stricter release processes, and
improving contributor experience.
There are many exciting features on the table as well, including:
* Improving dynamic inline evaluation.
* Container queries.
* Descendent styling ala a:hover p.
* tamagui build - build to plain css and JS for compatibility with
any bundler.
* Adding the native prop for platform-specific output on Select,
Dialog, Popover, Sheet and more.
* custom tokens.
* to show nice visualizations of what's happening inside
an entire tree of components.
* to automatically render skeleton variants for every
component in a tree.
* , or equivalent, ,
, and more.
* Adding pseudo focusWithinStyle.
* Breakpoints as token values allowing height="$lg".
wwwwwwwwwwwwwwwwwww
Sponsors
Tamagui is fully funded by Github Sponsors.
I love working on Tamagui, and there's a wealth of concrete and
interesting ways for it to mature. The motivation is to take things
slowly, do them right, and stay fully OSS.
In order to make this work, I'll need quite a few Subscribers. So,
I'm setting up a range of pretty cool Subscriber Benefits going
forward:
* Try new features as they are being developed.
* Private chat for subscribers to discuss and influence roadmap.
* Nightlies, access to a private npm org.
* Early access to upcoming Studio, Docs and other apps:
Docs
Your design system and UI auto-generates a beautiful docs site, just
like tamagui.dev.
[?] Studio
A frontend realtime editor for colors, themes, tokens, default styles
and more.
*****
A simpler way to build universal apps.
The Tamagui Studio, early beta
Docs can run alongside Studio in the future as part of a suite of
tools for your frontend. Studio will have realtime collaboration and
allows you to edit your apps themes live with HMR. Tweak colors,
themes, tokens, styles, icons, fonts, and more across every pseudo
state on every component.
Studio hooks into your app at runtime to achieve this thanks to the
big work we've aleady done pre-analyzing your components and their
types and props.
It will also export and import between JSON and Figma.
Please do consider sponsoring!
Sponsor
wwwwwwwwwwwwwwwwwww
Acknowledgements
We've had 38 new contributors since early this year, exponentially
increasing! Thanks to all of them, and also to the many QA testers
that join in our Discord chat every day :).
Early sponsors have made a huge difference: I owe a large thank you
to Vercel , Codingscape , QuestPortal and BeatGig , the first
corporate sponsors [?].
Tamagui draws inspiration and small areas of code from some
incredible other libraries: Radix provided much of the composable
component APIs, Floating UI handles all of the floating elements
positioning, Moti which served as a start point for the animation
drivers, and Framer Motion for the nifty AnimatePresence
functionality.
wwwwwwwwwwwwwwwwwww
Join the community
Twitter
Announcements and general updates.
Discord
Get involved and get questions answered.
GitHub
Issues, feature requests, and contributing.
Share this post on Twitter.
homepage#
by nate
built with Tamagui
Overview
IntroductionConfigurationGuides
Docs
InstallationThemesVariants
Community
Community
Blog
GitHub
Twitter
Discord