# Working in this repository
Guidance for contributors and coding agents changing emoji-picker-react itself. To *use* the picker in an application, read [docs/v5/AGENT_GUIDE.md](docs/v5/AGENT_GUIDE.md) and the packaged `llms-full.txt` instead.
## What this is
One emoji-picker engine with three public entries:
| Entry | File | Purpose |
| --- | --- | --- |
| `emoji-picker-react` | `src/index.tsx` | Batteries-included ``, enums, types. Registers the English dataset synchronously. |
| `emoji-picker-react/primitives` | `src/primitives/index.ts` | Composable parts (`Root`, `Search`, `SearchInput`, `CategoryNav`, `Viewport`, `List`, `Preview`, `SkinTone`, `Empty`, `Loading`, `LoadError`, `Panel`, `Reactions`), hooks and tokens. Loads the dataset on demand. |
| `emoji-picker-react/data` | `src/data.ts` | Framework-free `searchEmojis` / `getEmojiByUnified`. No React. |
The default picker is assembled from the primitives (`src/EmojiPickerReact.tsx`); there is one implementation of search, navigation, virtualization and selection. `test/primitives-architecture.test.ts` enforces this.
## Where things live
- `src/components/`, `src/hooks/`, `src/state/`, `src/DomUtils/`: the shared engine.
- `src/primitives/`: the public composition layer; `types.ts` is the prop contract, `nativeProps.ts` the forwarding rules, `scope.tsx` the development validation.
- `src/Stylesheet/`: ShipStyles sheets, zero-specificity tokens, cascade-layer support, once-per-document injection (`DedupedStyle.tsx`).
- `src/data/`, `src/data-core/`: generated datasets (`src/data/emojis-*.ts|json`, do not edit) and the pure search core.
- `docs/v5/` and the root `*.md` guides: user documentation (API, primitives, styling, data, migration, agent guide and prompts). Update them in the same commit as the behavior they describe; `npm run check:contracts` type-checks their `tsx check` examples.
- `test/` (Vitest, jsdom), `playwright/` (browser, visual, a11y), `stories/` (Storybook; `recipes/` are the 25 designs), `integration/` (real-consumer fixtures), `compat/` (type fixtures), `bench/`, `scripts/`.
- `example/`: Vite + React 19 app covering every usage path. `website/`: the Next.js demo site; `website/src/components/designs` and `website/public/recipes` are generated.
## Rules that tests enforce
- **React 16.8 floor.** `src` may only use React APIs available in 16.8 (`scripts/checkReactApiFloor.ts`); no `useId`, `useSyncExternalStore`, `use`, ``.
- **No default appearance in primitives.** Nothing under `src/primitives/` imports `components/main/defaultAppearance` or `EmojiPickerReact`; the packed primitives bundle is scanned for it.
- **Data entry is framework-free.** `src/data.ts` and `src/data-core/` never import React or ShipStyles.
- **Reserved attributes.** The library owns `role` and every `data-epr-*` attribute on managed elements; consumer props with those names are filtered.
- **Size and performance budgets.** `size-limit` caps every entry; `npm run check:pack-size` caps the published package (each dataset ships once as JSON and once as an ES module; no raw `.ts` sources); `npm run check:perf` compares against `bench/baseline.json`. Do not raise a budget to make a change fit.
- **Visual baselines are evidence.** Never bulk-update screenshots: review each actual/expected/diff image and update only the ones a deliberate change explains.
## Verify before you commit
```sh
npm run check:scripts && npm run lint && npm test && npm run build && npm run check:compat && npm run size && npm run check:pack-size
```
Browser suites: `npm run test:visual` and `npx playwright test --config playwright.behavior.config.ts` (after `npx playwright install --with-deps`). Packaging: `npm run check:package`, `npm run check:react16`. Packed-consumer e2e: `npm run test:e2e` (after `npm run build`) installs the tarball into a fresh Vite app, a CommonJS bundle a shadcn/ui app (via the real `shadcn add` against `website/public/r`) and the emoji-mart guide's adapter (extracted from the Markdown) under a strict CSP and records every test (`E2E_SLOWMO=250` paces recordings for review).
Repository automation is TypeScript and is checked by `npm run check:scripts`; see [scripts/README.md](scripts/README.md).
Generated files are regenerated, never edited: `npm run docs:llms` (after any documentation change), `npm run icons` (after an icon change), `npm run recipes` and `npm run designs` (after a recipe change), `npm run registry` (after `registry/emoji-picker.tsx`), `npm run build:data` (after data sources). The `docs` CI job fails on drift.
## Conventions
- Conventional Commits; a breaking change needs `feat!:` or a `BREAKING CHANGE:` footer (semantic-release cuts the version).
- Public prop and part names are semver API. Adding a behavior prop to `PickerProps` makes it reach `Root` automatically (`RootBehaviorProps` subtracts appearance props); update `PROPS.md` and `docs/v5/API.md`.
- Prefer `colorScheme` over `theme` in docs and examples (CSS-in-JS libraries reserve `theme`).
- Every user-facing string goes through `labels` (`src/config/config.ts`); never hard-code English in a component.
- Keep structural CSS (viewport overflow, cell geometry, category positioning) out of consumer-overridable surfaces; appearance goes through `--epr-*` tokens or `[data-epr-part]` selectors.