Monospace

Creating a New NUI App

Step-by-step guide to creating a new FiveM NUI application in the monorepo.

This guide walks you through creating a new NUI app from scratch using the shared Monospace packages.

Create the app directory

Create a new directory under apps/:

mkdir apps/my-nui-app
cd apps/my-nui-app

Create package.json

Add a package.json referencing the shared packages:

apps/my-nui-app/package.json
{
  "name": "my-nui-app",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc -b && vite build",
    "preview": "vite preview",
    "lint": "biome check --write"
  },
  "dependencies": {
    "@repo/ui": "workspace:*",
    "preact": "^10.28.3",
    "zod": "^4.3.6"
  },
  "devDependencies": {
    "@repo/vite-config": "workspace:*",
    "@repo/biome-config": "workspace:*",
    "@repo/tailwind-config": "workspace:*",
    "@repo/typescript-config": "workspace:*",
    "@preact/preset-vite": "^2.10.3",
    "autoprefixer": "^10.4.24",
    "postcss": "^8.5.6",
    "tailwindcss": "3",
    "typescript": "~5.9.3",
    "vite": "^7.3.1"
  }
}

Create Vite config

Import the shared Vite configuration:

apps/my-nui-app/vite.config.ts
import { preactConfig } from '@repo/vite-config'
import { defineConfig, type UserConfig } from 'vite'

export default defineConfig({
  ...(preactConfig as UserConfig),
})

This gives you the Preact preset and a relative base path (./) which is required for NUI embedding in FiveM.

Create Tailwind & PostCSS configs

Re-export the shared configurations:

apps/my-nui-app/tailwind.config.js
import tailwindcss from '@repo/tailwind-config/tailwind'

export default tailwindcss
apps/my-nui-app/postcss.config.js
import postcss from '@repo/tailwind-config/postcss'

export default postcss

Create TypeScript configs

apps/my-nui-app/tsconfig.json
{
  "files": [],
  "references": [
    { "path": "./tsconfig.app.json" },
    { "path": "./tsconfig.node.json" }
  ]
}
apps/my-nui-app/tsconfig.app.json
{
  "extends": "@repo/typescript-config/preact-library.json",
  "compilerOptions": {
    "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
    "paths": {
      "react": ["./node_modules/preact/compat/"],
      "react-dom": ["./node_modules/preact/compat/"]
    }
  },
  "include": ["src"]
}

Create Biome config

apps/my-nui-app/biome.json
{
  "extends": ["@repo/biome-config/biome"]
}

Create the entry point

apps/my-nui-app/index.html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>My NUI App</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>
apps/my-nui-app/src/index.css
@import '@repo/tailwind-config/style';
apps/my-nui-app/src/main.tsx
import { render } from 'preact'
import './index.css'
import { App } from './app.tsx'

const element = document.getElementById('app')!
render(<App />, element)
apps/my-nui-app/src/app.tsx
import { Button } from '@repo/ui/components/button'

export function App() {
  return (
    <>
      <Button>Hello World</Button>
    </>
  )
}

Install and run

From the monorepo root:

bun install
bun run dev

The @repo/vite-config sets base: './' to produce relative asset paths. This ensures the built NUI works correctly when embedded in FiveM, where assets are served from a local file path.

On this page