TutorialsNov 10, 202512 min read

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.

Alex Chen
Alex Chen
Lead Game Developer
Creating a Game Server UI with React

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

Need custom UI development for your server?

Share this article:
Alex Chen
Alex Chen
Lead Game Developer

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

Related Articles

Continue reading with these related posts.

Need Help With Your Project?

Our team of experts is ready to help you build something amazing. Let's discuss your ideas and bring them to life.