Setting up a project
What init changes, what it leaves alone, and the three things it hands back to you.
bunx delacour@latest initMost projects never run this
add does everything on this page the first time it is used, with
the layout inferred. Run init yourself for the two decisions inference cannot make: putting the
components in a package several apps share, and a source directory this project spells its own way.
Two questions, both skippable with --defaults: where the components should live, and what your
source directory is called. Everything else is read off the project — the package manager from the
lockfile, the Expo SDK from package.json, the path aliases from tsconfig.json.
You can add components in the same run:
bunx delacour@latest init button input fieldOn a project that already has a native-components.json, init says so and does not rewrite it —
--force is how you mean it. Component names given alongside are still added, as a plain add:
a file you have edited is a conflict it asks about, rather than one it replaces.
What it changes
native-components.json
Written next to your components. In a monorepo that is the package you ran it from — see Monorepos.
metro.config.js
Wrapped with Uniwind's config, outermost:
const { withUniwindConfig } = require("uniwind/metro");
const { getDefaultConfig } = require("expo/metro-config");
const config = getDefaultConfig(__dirname);
module.exports = withUniwindConfig(config, {
cssEntryFile: "./src/styles/global.css",
dtsFile: "./src/uniwind-types.d.ts",
});If there is no Metro config, one is written. If the export is not a shape the CLI can rewrite safely, it prints the snippet and asks you to add it rather than guessing at an unfamiliar file.
Outermost is load-bearing
withUniwindConfig installs the transformer that compiles className into styles. A wrapper
applied after it can replace config.transformer, and every component then renders unstyled
with nothing logged. delacour doctor checks the ordering, not just the presence.
Your Tailwind entry
An @import for the theme and one @source per directory holding copied code, inside a marked
block:
@import "tailwindcss";
@import "uniwind";
@source "../**/*.{ts,tsx}";
/* delacour:start — managed by `delacour init`, edit outside this block */
@import "./index.css";
@source "../components/ui";
@source "../hooks";
@source "../lib";
/* delacour:end */Anything you write outside the markers is left alone, and re-running init replaces the block
rather than appending a second one.
The theme, and the provider
init adds the styles item — tokens.css, theme.css, base.css and the uniwind-env.d.ts
type shim — and installs tailwindcss and uniwind. It adds the provider item too, so
DelacourProvider is in your ui directory before you mount it.
theme.css is the one of those that is yours. Replace it whole with the theme.css tab from
Theme, or paste a shadcn globals.css over it and run delacour theme. The rest is
plumbing and re-runs of init leave it alone.
What it does not change
tsconfig.json and app.config.ts are read, never written.
For tsconfig.json that is a deliberate trade. The CLI looks for a wildcard alias like
"@/*": ["./src/*"] and, if it finds one, records the matching alias per directory. If it finds
none, imports are written as relative paths instead — which Metro resolves whether or not
experiments.tsconfigPaths is on. Creating an alias you did not ask for is a larger change than
the imports it saves, and in a monorepo the same alias often already means something else.
The three things left for you
init prints these, and delacour doctor checks all of them afterwards.
Import the CSS entry
import "@/styles/global.css";First statement of the root layout. withUniwindConfig's cssEntryFile names the file for the
transformer; it does not put it in the bundle. Skip this and the app builds, boots and renders
every component completely unstyled, with nothing logged.
Mount DelacourProvider
Every pressable in the library is a Gesture Handler detector, and a detector outside a
GestureHandlerRootView never receives a touch. Nothing throws — the button just does not respond.
DelacourProvider is that root, plus the other layers the components read from, and init has
already copied it in.
import "@/styles/global.css";
import { DelacourProvider } from "@/components/ui/provider";
import { Stack } from "expo-router";
export default function RootLayout() {
return (
<DelacourProvider>
<Stack />
</DelacourProvider>
);
}See Provider for what each of its layers is doing.
delacour doctor passes on either DelacourProvider or a hand-mounted GestureHandlerRootView.
Turn on tsconfigPaths — only if you use aliases
experiments: {
tsconfigPaths: true,
}Metro does not read tsconfig path mappings without it, so @/components/ui/button fails to
resolve. If native-components.json has an empty aliases object, your imports are relative and this does
not apply.
Rebuild after a native module lands
add tells you when it installed one. A JavaScript reload will not pick it up — the module is not
in the binary yet.
npx expo run:ios # or run:androidVerify
bunx delacour@latest add buttonbunx delacour@latest doctorimport { Button } from "@/components/ui/button";
<Button onPress={() => console.log("pressed")}>Press me</Button>;A string child is wrapped in a Button.Label for you. If the button renders but does not respond,
DelacourProvider is missing above it.