# Installation

Add the runtime, configure Unistyles and Reanimated, and copy your first component.

[Documentation](https://eoria.adamtrip.pt/start/installation/)

Five minutes on an existing Expo project. New projects can start from
`npx create-expo-app@latest` with the default template.

1. **Run init.**

   ```sh
   npx @eoria/cli init
   ```

   It writes `eoria.json` and `src/unistyles.ts`, adds `import '@/unistyles'` as the first line
   of your root layout, writes a Babel config with the Unistyles and Worklets plugins, adds the
   `@/*` alias, and installs `@eoria/core` and the peers with `expo install`. Each file it would
   overwrite is skipped and reported. The [CLI page](https://eoria.adamtrip.pt/start/cli.md#init) lists exactly what it
   touches.

2. **Check the theme import.**

   `src/unistyles.ts` registers the themes and has to run before any component renders. `init`
   imports it at the top of `app/_layout.tsx`, `App.tsx` or `index.js`, whichever it finds. If
   your entry file lives somewhere else, add the import yourself and keep it first.

   ```ts title="src/unistyles.ts"
   import { configureUnistyles, defaultThemes, type EoriaTheme } from '@eoria/core'

   declare module 'react-native-unistyles' {
     export interface UnistylesThemes {
       light: EoriaTheme
       dark: EoriaTheme
     }
   }

   configureUnistyles({ themes: defaultThemes })
   ```

   `configureUnistyles` wraps `StyleSheet.configure` and turns on adaptive themes, so the app
   follows the OS appearance until you say otherwise. See [Theming](https://eoria.adamtrip.pt/start/theming.md) for presets
   and manual switching.

3. **Add your first components.**

   eoria CLI

   ```sh
   npx @eoria/cli add button portal toast
   ```

   shadcn CLI

   ```sh
   npx shadcn@latest add https://eoria.adamtrip.pt/r/button.json
   ```

   Dependencies resolve transitively. Adding `button` also copies `text`; adding `select` copies
   `popper`, `portal` and `text`.

4. **Mount the portal host and toaster.**

   Overlays render through a portal so they sit above navigation. Put the host last in your
   root layout.

   ```tsx title="src/app/_layout.tsx"
   import '@/unistyles'
   import { Stack } from 'expo-router'
   import { PortalHost } from '@/components/ui/portal'
   import { Toaster } from '@/components/ui/toast'

   export default function RootLayout() {
     return (
       <>
         <Stack />
         <PortalHost />
         <Toaster />
       </>
     )
   }
   ```

5. **Rebuild the native app.**

   ```sh
   npx expo prebuild
   npx expo run:ios
   ```

   Expo Go will not work. If the app throws \`Unistyles: One of your stylesheets is trying to

   get the theme, but no theme has been selected yet\`, the themes file is not imported before
   the first component renders. Check step 2.

## Peer versions

The only hard requirement is the New Architecture. These are the versions the example app and
the previews run on:

| Package                    | Version |
| -------------------------- | ------- |
| react-native               | 0.86    |
| react-native-unistyles     | 3.3     |
| react-native-reanimated    | 4.5     |
| react-native-worklets      | 0.10    |
| react-native-nitro-modules | 0.37    |

These are what the example app pins. Older Unistyles 3 releases should work; Unistyles 2 will
not, because the recipe engine uses `useVariants` and the babel plugin's dependency tracking.

## Jest

`@eoria/core/jest` mocks Unistyles with variant resolution. The official mock strips variants,
which makes every variant test pass for the wrong reason.

```js title="jest.config.js"
module.exports = {
  preset: 'jest-expo',
  setupFiles: ['@eoria/core/jest'],
}
```

## Vitest

`@eoria/core/vitest` registers the same mock with `vi.mock`. Add it to `setupFiles` and put
`@eoria/core` in `server.deps.inline`:

```ts title="vitest.config.ts"
import { defineConfig } from 'vitest/config'

export default defineConfig({
  test: {
    setupFiles: ['@eoria/core/vitest'],
    server: { deps: { inline: ['@eoria/core'] } },
  },
})
```

The inline entry matters. Vitest hands packages in `node_modules` to Node's own loader, and
`vi.mock` never sees the imports that happen there. Without it, `@eoria/core` loads the real
Unistyles, which fails on `react-native`. Vitest does not run React Native itself, so mock or
alias the other native modules your tests reach.

The entry is ESM only. Vitest imports `setupFiles` as ES modules, so this needs nothing from
you, but a CommonJS `require('@eoria/core/vitest')` fails.
