Installation
Knit UI is two packages: @knitui/core (tokens, themes, Provider) and
@knitui/components (the components). Everything else is optional.
Four steps, about five minutes. Pick your framework in any tab below — every tab on this page stays in sync, and so does your package manager, so you only choose once.
What you need first
| Requirement | Version |
|---|---|
| Node | 20 or newer |
| React | 19.2.x |
| Native app | React Native 0.86.x — easiest via Expo SDK 57 |
| Web app | Next.js 16 (built with --webpack) or Vite |
Anything else is checked for you: if a peer dependency is missing, the kit throws a named error the first time a component renders instead of failing somewhere deep inside a gesture handler. Full matrix in Compatibility.
1. Install the packages
npx expo install @knitui/core @knitui/components react-native-gesture-handler react-native-reanimated react-native-svg react-native-teleport react-native-workletsNote the expo install: it picks the native module versions your Expo SDK was
built against. Installing those peers by hand is the single most common cause of a
red screen on first launch. (npx expo install --check audits a project later.)
No app yet? npx create-expo-app@latest my-app first.
npm install @knitui/core @knitui/components @knitui/plugins react-native-web react-native-gesture-handler react-native-reanimated react-native-svg react-native-teleport react-native-workletsreact-native-web maps the React Native primitives onto the DOM, and
@knitui/plugins carries the build integration plus the SSR provider you'll wire
in step 2.
No app yet? npx create-next-app@latest my-app first.
npm install @knitui/core @knitui/components @knitui/plugins react-native-web react-native-gesture-handler react-native-reanimated react-native-svg react-native-teleport react-native-workletsNo app yet? npm create vite@latest my-app -- --template react-ts first.
npm install @knitui/core @knitui/components react-native-gesture-handler react-native-reanimated react-native-svg react-native-teleport react-native-workletsThen link the native side and rebuild — a Metro reload is not enough once native modules are added:
cd ios && pod install && cd ..
npm run ios # and: npm run android2. Point your bundler at the kit
The kit source-ships — main points at ./src/index.ts, so platform-split
files stay resolvable and the Tamagui compiler can see real source. Your bundler
needs to know that, which is the one job of @knitui/plugins.
module.exports = (api) => {
api.cache(true);
return {
presets: ["babel-preset-expo"],
plugins: [require("@knitui/plugins/babel-plugin")],
};
};const { getDefaultConfig } = require("expo/metro-config");
const { withKnitui } = require("@knitui/plugins/metro");
module.exports = withKnitui(getDefaultConfig(__dirname));Restart with a cleared cache the first time: npx expo start --clear.
import { withKnitui } from "@knitui/plugins/next-plugin";
export default withKnitui({
reactStrictMode: true,
transpilePackages: [
"react-native-web",
"react-native-reanimated",
"react-native-gesture-handler",
"@knitui/core",
"@knitui/components",
// …plus any satellite package you add later
],
});{
"scripts": {
"dev": "next dev --webpack",
"build": "next build --webpack"
}
}import { knitui } from "@knitui/plugins/vite";
import react from "@vitejs/plugin-react";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [react(), knitui()],
});knitui() sets the platform target, prefers .web.* files, aliases
react-native → react-native-web, and pre-bundles the packages that ship
untranspiled TypeScript.
module.exports = {
presets: ["module:@react-native/babel-preset"],
plugins: [require("@knitui/plugins/babel-plugin")],
};const { getDefaultConfig } = require("@react-native/metro-config");
const { withKnitui } = require("@knitui/plugins/metro");
module.exports = withKnitui(getDefaultConfig(__dirname));Restart Metro with npm start -- --reset-cache the first time.
Details on what each plugin does: Bundler setup.
3. Wrap your app in one Provider
import { Provider } from "@knitui/core";
import { Stack } from "expo-router";
export default function RootLayout() {
return (
<Provider defaultColorScheme="system">
<Stack />
</Provider>
);
}Two providers, one job each: NextTamaguiProvider flushes the generated CSS into
the server HTML, Provider is the runtime context — and it has to be a client
component.
import { NextTamaguiProvider } from "@knitui/plugins/next";
import { Providers } from "./providers";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<NextTamaguiProvider>
<Providers>{children}</Providers>
</NextTamaguiProvider>
</body>
</html>
);
}"use client";
import { Provider } from "@knitui/core";
export function Providers({ children }: { children: React.ReactNode }) {
return <Provider defaultColorScheme="system">{children}</Provider>;
}More on hydration and what stays client-only: Server rendering.
import { Provider } from "@knitui/core";
import { createRoot } from "react-dom/client";
import { App } from "./App";
createRoot(document.getElementById("root")!).render(
<Provider defaultColorScheme="system">
<App />
</Provider>,
);import { Provider } from "@knitui/core";
export default function App() {
return (
<Provider defaultColorScheme="system">
<RootNavigator />
</Provider>
);
}Provider is more than a theme context. It mounts the token and theme config, the
color-scheme controller ("system" follows the OS), GestureHandlerRootView —
so you don't add one — and the portal host that Modal, Drawer, Popover and
Tooltip teleport into.
Deeper per-framework detail — dev builds, static export, Storybook, the pitfalls specific to each: Expo · Next.js · Vite.
4. Check it works
Drop this anywhere inside the Provider and run the app:
import { Button, Card, Group, Text, TextInput } from "@knitui/components";
export function SignIn() {
return (
<Card padding="lg" gap="$md">
<Text fontWeight="600">Sign in</Text>
<TextInput label="Email" placeholder="you@example.com" />
<Group>
<Button variant="light">Cancel</Button>
<Button>Continue</Button>
</Group>
</Card>
);
}You should see a bordered card with a labelled input and two buttons, themed to your OS light/dark setting, on iOS, Android and the web from that one file. If it renders, you're installed — and the buttons below are the same components, running live in this page:
Rendered by the docs site with the setup you just did.
Loading example…
Add only what you need
The core two packages cover 100+ components. Each satellite package is installed
on its own, and only @knitui/map, @knitui/media and @knitui/graphics bring
native modules with them.
Calendars, date and time pickers, date inputs.
Gesture-driven carousel with ten layout modes.
Bottom sheets with a pure-TS snap engine.
Audio and video players, playlists, recording, DSP.
MapLibre on native and web behind one API.
Skia canvases, effects and visualizers.
~6,100 Tabler icons, one path each.
The Noto emoji set as components.
On Expo, install a satellite package's native module through expo install too:
npx expo install @knitui/media expo-audio expo-videoWhich package needs which peer, straight from each package.json:
| Package | Required peers | Optional peers |
|---|---|---|
@knitui/core | react react-native react-native-gesture-handler react-native-reanimated | react-native-web |
@knitui/components | react react-native react-native-svg react-native-teleport | react-native-web react-native-reanimated react-native-worklets react-native-gesture-handler |
@knitui/hooks | react react-native | react-native-reanimated |
@knitui/icons | react | react-native react-native-svg |
@knitui/emoji | react | react-native react-native-svg |
@knitui/dates | react react-native react-native-teleport | react-native-web react-native-reanimated |
@knitui/carousel | react | react-native react-native-gesture-handler react-native-reanimated react-native-worklets |
@knitui/map | @maplibre/maplibre-gl-style-spec @types/geojson react | @maplibre/maplibre-react-native maplibre-gl react-dom react-native react-native-svg |
@knitui/media | react | react-native react-native-reanimated |
@knitui/graphics | @shopify/react-native-skia react react-native | react-native-reanimated |
@knitui/sheet | react | react-native react-native-gesture-handler react-native-reanimated react-native-worklets |
@knitui/mediaquery | react | react-native |
@knitui/plugins | react react-native | next |
If something went wrong
| What you see | Almost always |
|---|---|
Red screen, "property is not writable" on Hermes | Two copies of react-native |
"Another instance of Reanimated was detected" | Two copies of reanimated |
| Styles land a frame late on Next.js | NextTamaguiProvider missing or too deep |
| A modal or dropdown renders behind everything | More than one Provider |
Warning: Tamagui skipped loading N modules | Harmless — src-shipped packages |
| Sliders or sheets don't respond to drags | Something is mounted above Provider, so there's no GestureHandlerRootView |
Every symptom, with fixes: Troubleshooting.