# Fivem React Nui

> Builds NUI interfaces for FiveM using React 18 + TypeScript + Vite + Tailwind CSS v3 + Zustand. Use when the user mentions NUI, interface, UI, menu, HUD, panel, overlay, React, Vite, Tailwind, or any in-game interface for FiveM — regardless of the backend framework (vRP, QBCore, Qbox, ESX). Covers project structure, FiveM CEF restrictions, observe/Post hooks, VisibilityProvider, animations, dynamic config/theme, and performance rules.

- Skill: `proelias7/fivem-react-nui` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add proelias7/fivem-react-nui`
- Raw SKILL.md: https://api.skillmd.com/api/skills/proelias7/fivem-react-nui/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: proelias7 (https://skillmd.com/u/proelias7)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/proelias7/fivem-react-nui

---


# FiveM NUI — React + Vite

**Language rule:** Internal reasoning is in compact English. All messages and output displayed to the user must be in the user's language.

Stack: **React 18 + TypeScript + Vite + Tailwind CSS v3.4.17 + Zustand**

## Fundamental Rules

- `base: "./"` in `vite.config.ts` — **MANDATORY** for assets to load in FiveM
- **Never** fix Vite `rollupOptions.output` filenames without `[hash]` — FiveM CEF caches NUI; fixed names (`assets/[name].css`) leave stale CSS for some players
- **Overlay/shell fill on transparent html:** opaque **hex** + `background-image: linear-gradient(#111,#111)` — **never** `rgba()` / Tailwind `bg-*/70` / `opacity` on the **same** rounded element as the panel fill (CEF bug on some iGPUs). Screen dim = sibling layer **without** `border-radius` (`::before` inset-0). Toggle with `display: flex|none` — **never** jQuery `fadeIn`/`fadeOut` on the overlay container
- Use `rem` for ALL sizes — NEVER `px` for layout (scales with player resolution)
- **Tailwind v4 uses OKLCH**, which FiveM CEF does not support — **use Tailwind v3.4.17**
- FORBIDDEN: `backdrop-filter: blur()`, `filter: blur()`, `filter: drop-shadow()` — cause FPS drop
- FORBIDDEN: framer-motion, GSAP, react-spring — use pure CSS transitions/keyframes
- Global `overflow: hidden` and `user-select: none`
- Independent modules (Notify, Progress, HUD) outside `VisibilityProvider`
- Main interface (panels, crafts, dialogs) inside `VisibilityProvider` with `SetNuiFocus`
- `isEnvBrowser()` for data mocking in dev
- Communication: `observe()` to listen to NUI messages, `Post.create()` to send callbacks

## Architecture

```tsx
// main.tsx
ReactDOM.createRoot(document.getElementById("root")!).render(
  <ThemeProvider>
    {/* Always visible — do not block game input */}
    <NotifyComponent />
    <ProgressComponent />

    {/* Controlled by VisibilityProvider — requires NuiFocus */}
    <HashRouter>
      <VisibilityProvider>
        <AppContent />
      </VisibilityProvider>
    </HashRouter>
  </ThemeProvider>
);
```

## NUI Hooks

### observe — Listen to Lua messages
```typescript
observe<NotifyData>("module:notify", (data) => {
  addNotify(data);
});
```

### Post — Send callbacks to Lua
```typescript
await Post.create("buy", { item: "water", qty: 1 });
```

### Lua side
```lua
-- Open
SendNUIMessage({ action = "setNui", nui = "panel", data = { ... } })
SetNuiFocus(true, true)

-- Close callback
RegisterNUICallback("removeFocus", function(data, cb)
    SetNuiFocus(false, false)
    cb("ok")
end)
```

## fxmanifest.lua Integration

```lua
ui_page "src/ui/build/index.html"

files {
    "src/ui/build/index.html",
    "src/ui/build/**/*",           -- covers index-[hash].js/css
    "src/ui/project/public/**/*",
}
```

## Recommended Dependencies

```json
{
  "dependencies": {
    "react": "^18", "react-dom": "^18",
    "react-router-dom": "^7", "zustand": "^4",
    "clsx": "^2", "tailwind-merge": "^3",
    "lucide-react": "^0.5", "tailwindcss": "^3",
    "autoprefixer": "^10", "postcss": "^8"
  },
  "devDependencies": {
    "@vitejs/plugin-react-swc": "^3",
    "typescript": "^5", "vite": "^5"
  }
}
```

Avoid: MUI, Chakra UI, Ant Design, framer-motion, styled-components — all too heavy for FiveM CEF.

## fxmind agent vision (NUI dump)

Prefer structured state over screenshots. The **agent** wires and unwires — do not ask the user to patch scripts:

1. `fxmind_fivem_nui_wire` `{ resource }` — patches fxmanifest + injects DOM probe into `ui_page`
2. `ensure` bridge + resource; user opens NUI in-game
3. `fxmind_fivem_nui_dump`
4. **`fxmind_fivem_nui_unwire`** before finishing (mandatory cleanup)

Optional permanent integration (not required for agent debug): `registerFxmindNuiDump(() => useStore.getState())` from bridge snippets.

For the full implementation guide (project structure, vite.config, responsive system, CSS restrictions, hooks source, Visibility/Animation/Theme providers, debugger): [ui-guide.md](ui-guide.md)

