Creating a Game Server UI with React
A step-by-step tutorial on building a modern, responsive UI for your FiveM server using React and NUI.


Building Modern Game Server UIs
FiveM's NUI (New User Interface) system allows you to create stunning React-based interfaces. In this tutorial, we'll build a complete inventory system UI from scratch.
Prerequisites
- Basic React knowledge
- Understanding of FiveM resource structure
- Node.js installed locally
Project Setup
First, create a new React project inside your FiveM resource:
npx create-react-app ui --template typescript
cd ui
npm install framer-motion lucide-react
Setting Up NUI Communication
Create a utility for NUI message handling:
// src/utils/nui.ts
export const fetchNui = async <T>(eventName: string, data?: unknown): Promise<T> => {
const resp = await fetch(`https://${GetParentResourceName()}/${eventName}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
})
return resp.json()
}
export const useNuiEvent = <T>(action: string, handler: (data: T) => void) => {
useEffect(() => {
const eventListener = (event: MessageEvent) => {
if (event.data.action === action) {
handler(event.data.data)
}
}
window.addEventListener('message', eventListener)
return () => window.removeEventListener('message', eventListener)
}, [action, handler])
}
Building the Inventory Grid
// src/components/InventoryGrid.tsx
import { motion } from 'framer-motion'
interface Item {
id: string
name: string
quantity: number
icon: string
}
interface InventoryGridProps {
items: Item[]
slots: number
onItemClick: (item: Item) => void
}
export function InventoryGrid({ items, slots, onItemClick }: InventoryGridProps) {
return (
<div className="grid grid-cols-5 gap-2 p-4">
{Array.from({ length: slots }).map((_, index) => {
const item = items[index]
return (
<motion.div
key={index}
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
className="aspect-square bg-black/50 rounded-lg border border-white/10
flex items-center justify-center cursor-pointer
hover:border-violet-500/50 transition-colors"
onClick={() => item && onItemClick(item)}
>
{item && (
<div className="text-center">
<img src={item.icon} alt={item.name} className="w-12 h-12 mx-auto" />
<span className="text-xs text-white/80">{item.name}</span>
<span className="text-xs text-violet-400 block">x{item.quantity}</span>
</div>
)}
</motion.div>
)
})}
</div>
)
}
Connecting to Lua
On the Lua side, create the NUI callbacks:
-- client.lua
RegisterNUICallback('getInventory', function(data, cb)
local inventory = GetPlayerInventory()
cb(inventory)
end)
RegisterNUICallback('useItem', function(data, cb)
TriggerServerEvent('inventory:useItem', data.itemId)
cb({ success = true })
end)
-- Opening the UI
RegisterCommand('inventory', function()
SetNuiFocus(true, true)
SendNUIMessage({
action = 'openInventory',
data = {}
})
end)
Styling Tips
For game UIs, embrace the dark theme and add atmospheric effects:
/* Custom glass effect */
.glass-panel {
background: rgba(0, 0, 0, 0.7);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
}
/* Subtle glow on hover */
.item-slot:hover {
box-shadow: 0 0 20px rgba(139, 92, 246, 0.3);
}
/* Scanline effect for retro feel */
.scanlines::before {
content: '';
position: absolute;
inset: 0;
background: repeating-linear-gradient(
0deg,
rgba(0, 0, 0, 0.1),
rgba(0, 0, 0, 0.1) 1px,
transparent 1px,
transparent 2px
);
pointer-events: none;
}
Performance Considerations
- Use React.memo for item components
- Implement virtualization for large inventories
- Debounce frequent updates
- Lazy load item icons
Building for Production
npm run build
Move the build output to your resource's html folder and update your fxmanifest.lua:
ui_page 'html/index.html'
files {
'html/index.html',
'html/static/css/*.css',
'html/static/js/*.js'
}
Next Steps
This foundation can be extended to:
- Drag and drop functionality
- Item context menus
- Trading interfaces
- Crafting systems

Passionate about building great digital experiences. When not coding, you can find me exploring new technologies and sharing knowledge with the community.

