native-components.json

Where components go, how they import each other, and which app to wire up.

Written by init, next to your components. The nearest one wins, walking up from --cwd.

native-components.json
{
  "$schema": "https://raw.githubusercontent.com/delacournz/delacour-ui/main/registry/config.schema.json",
  "framework": "expo",
  "typescript": true,
  "registry": {},
  "registries": {},
  "paths": {
    "ui": "src/components/ui",
    "lib": "src/lib",
    "hooks": "src/hooks",
    "styles": "src/styles",
    "icons": "src/lib/icons"
  },
  "aliases": {
    "ui": "@/components/ui",
    "lib": "@/lib",
    "hooks": "@/hooks",
    "styles": "@/styles",
    "icons": "@/lib/icons"
  },
  "app": {
    "root": ".",
    "css": "src/styles/global.css",
    "metroConfig": "metro.config.js",
    "uniwindTypes": "src/uniwind-types.d.ts"
  }
}

The $schema URL is generated from the same definitions the CLI validates against, so editor completions cannot describe a shape the CLI would then reject.

paths and aliases are not the same thing

This is the one part of the file worth understanding. They answer two different questions:

  • paths — where a file lands on disk, relative to native-components.json.
  • aliases — what other files import it as.

They coincide only when the project has path aliases configured. Many Expo apps do not, and Metro resolves tsconfig paths only with experiments.tsconfigPaths turned on. Keeping them separate means two things: add never has to parse a tsconfig to work out where anything went, and a project with no aliases at all still gets imports that resolve.

An empty aliases object is a valid configuration

Leave a namespace out and imports into it are written as relative paths, computed from where the files actually landed. Delete aliases entirely and every import is relative.

// aliases.ui set
import { Icon } from "@/components/ui/icon";

// aliases.ui absent
import { Icon } from "../icon";

init fills aliases in by reading a wildcard mapping like "@/*": ["./src/*"] out of tsconfig.json. It never writes to that file.

The five namespaces

Prop

Type

package

{ "package": { "name": "@acme/ui" } }

Present only when the components live in a shared package, and written by init when you name one. Its presence is what makes add maintain the package's exports map and record native modules as peers — see Monorepos.

Prop

Type

app

Which Expo app to wire up. Every path here is relative to app.root, and app.root itself is relative to native-components.json.

Prop

Type

This block exists because the components and the app are not always the same package. Metro, the Tailwind entry and the native dependencies always belong to the app, even when the components live in a shared package — see Monorepos.

registry

{
  "registry": { "ref": "v0.2.0" }
}

Prop

Type

Covered in The registry.

The rest

Prop

Type

Changing it later

Edit the file and re-run add for anything already copied — the CLI writes to whatever paths says now, so old files stay where they were. delacour info prints the resolved absolute paths, which is the quickest way to check a change landed the way you meant.

On this page