A modern, highly optimized JavaScript-based Blip Management System for FiveM servers. Features a beautiful in-game NUI Dashboard to create, edit, search, and manage map blips in real-time — no server restart required.
| Feature | Description |
|---|---|
| 🎨 Modern Dashboard UI | Clean, responsive interface built with pure HTML, vanilla CSS, and optimized JavaScript |
| 🔍 Searchable Dropdowns | Instant search across 957 GTA V blip sprites and 86 blip colors with live previews |
| 🔄 Auto Framework Detection | Automatically detects and integrates with your active framework |
| 👮 Job Restriction | Restrict blip visibility to specific player jobs (e.g. police, ambulance) — auto-updates on job change |
| 🟢 Area Radius Zones | Transparent colored circle or square zones (0–500 meters) around blip coordinates |
| ⏱️ Auto-Expiring Blips | Set countdown timers (5 min to 24 hours) so blips automatically delete themselves |
| 🔐 License Whitelist | Allow specific Rockstar licenses to access the editor without framework admin roles |
| 🗺️ Map Controls | Teleport to blip coords, fetch live player position, configure flashes & GPS routing |
| ⌨️ Keyboard Friendly | Quickly close the dashboard using the ESC key |
| 💾 Persistent Storage | Blips are saved to data/blips.json and persist across server restarts |
| Framework | Auto-Detected | Permission System | Job Sync |
|---|---|---|---|
QBCore (qb-core) |
✅ | QBCore.Functions.HasPermission |
✅ |
Qbox (qbx_core) |
✅ | qbx_core:HasPermission |
✅ |
ESX (es_extended) |
✅ | xPlayer.getGroup() |
✅ |
Ox Core (ox_core) |
✅ | player.getGroup() |
✅ |
| Standalone | ✅ (Fallback) | Ace Permissions + License Whitelist | ❌ |
Note: The bridge system automatically detects your framework on resource start. No manual configuration needed — just drop the resource in and go.
-
Download or clone this repository into your server's
resourcesfolder:resources/ └── jinn-blips/ -
Add the resource to your
server.cfg:ensure jinn-blips
-
(Optional) Configure permissions in
config.lua— see Configuration below. -
Restart your server or run
ensure jinn-blipsin the server console.
| Command | Description |
|---|---|
/blip |
Opens the Blips Dashboard (requires admin permission) |
Edit config.lua to customize access control:
JinnBlipsConfig = {
-- Required permission group to manage blips.
-- Set to 'admin' for admin-only access.
-- Set to 'none' to allow everyone.
adminPermission = 'admin',
-- Whitelist specific Rockstar licenses to bypass framework permission checks.
-- These players can always access the dashboard regardless of their group.
allowedLicenses = {
-- "license:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
-- "license:yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"
}
}The system checks permissions in this order:
- License Whitelist — If the player's Rockstar license is in
allowedLicenses, access is granted immediately. - Framework Permissions — Checks the player's group/role using the active framework's API.
- Ace Permissions Fallback — Checks for
command.bliporgroup.adminace permissions.
Tip: To grant a specific player ace permission, add this to your
server.cfg:add_ace identifier.license:xxxx command.blip allow
These exports can be called from any client-side script (Lua or JavaScript) to manage blips programmatically.
Creates and renders a new blip on the map.
Syntax:
exports['jinn-blips']:createBlip(id, coords, sprite, color, name, options)
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
string |
✅ | Unique identifier for the blip |
coords |
vector3 / table |
✅ | {x, y, z} coordinates |
sprite |
number |
✅ | GTA V blip sprite ID (reference) |
color |
number |
✅ | GTA V blip color ID |
name |
string |
✅ | Display name shown on the map |
options |
table |
❌ | Additional configuration (see below) |
Options Table:
| Key | Type | Default | Description |
|---|---|---|---|
scale |
number |
1.0 |
Blip size (0.5 to 2.0) |
flash |
boolean |
false |
Enable blip flashing animation |
route |
boolean |
false |
Show GPS route line to blip |
shortRange |
boolean |
true |
Only show blip when nearby on minimap |
job |
string |
"" |
Restrict visibility to a specific job (empty = all) |
radius |
number |
0 |
Area zone radius in meters (0 = disabled) |
showCenter |
boolean |
true |
Show the center sprite icon |
shape |
string |
"circle" |
Zone shape: "circle" or "square" |
opacity |
number |
30 |
Zone opacity percentage (0–100) |
expireAt |
number|nil |
nil |
Timestamp (ms) when blip auto-deletes |
skipPersist |
boolean |
false |
If true, blip won't be saved to file |
Lua Example:
-- Basic blip
exports['jinn-blips']:createBlip(
'my_garage',
vector3(-350.0, -120.0, 38.0),
357,
47,
'Central Garage'
)
-- Advanced blip with all options
exports['jinn-blips']:createBlip(
'police_checkpoint',
vector3(450.0, -980.0, 30.5),
137,
38,
'Police Checkpoint',
{
scale = 0.9,
flash = true,
route = true,
shortRange = false,
job = 'police',
radius = 100,
showCenter = true,
shape = 'circle',
opacity = 25,
expireAt = GetGameTimer() + (5 * 60 * 1000) -- expires in 5 minutes
}
)
-- Temporary event blip (no job restriction, expires in 10 minutes)
exports['jinn-blips']:createBlip(
'race_event',
vector3(-1030.0, -2730.0, 20.0),
315,
5,
'Street Race Event',
{
scale = 1.2,
flash = true,
route = true,
expireAt = GetGameTimer() + (10 * 60 * 1000)
}
)JavaScript Example:
// Basic blip
exports['jinn-blips'].createBlip(
'my_garage',
{ x: -350.0, y: -120.0, z: 38.0 },
357,
47,
'Central Garage'
);
// Advanced blip with zone & expiration
exports['jinn-blips'].createBlip(
'police_checkpoint',
{ x: 450.0, y: -980.0, z: 30.5 },
137,
38,
'Police Checkpoint',
{
scale: 0.9,
flash: true,
route: true,
shortRange: false,
job: 'police',
radius: 100,
showCenter: true,
shape: 'circle',
opacity: 25,
expireAt: Date.now() + (5 * 60 * 1000)
}
);Updates one or more properties of an existing blip.
Syntax:
exports['jinn-blips']:modifyBlip(id, modifications)
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
string |
✅ | The blip's unique ID |
modifications |
table |
✅ | Key-value pairs of properties to update |
Returns: boolean — true if modified successfully, false if the blip ID doesn't exist.
Lua Example:
-- Change the name and color
exports['jinn-blips']:modifyBlip('my_garage', {
name = 'Premium Garage',
color = 5
})
-- Move a blip to new coordinates and make it flash
exports['jinn-blips']:modifyBlip('police_checkpoint', {
coords = vector3(500.0, -1000.0, 32.0),
flash = true,
sprite = 58
})
-- Add an area zone to an existing blip
exports['jinn-blips']:modifyBlip('my_garage', {
radius = 80,
shape = 'square',
opacity = 40
})
-- Change job restriction
exports['jinn-blips']:modifyBlip('police_checkpoint', {
job = 'ambulance'
})JavaScript Example:
// Change the name and color
exports['jinn-blips'].modifyBlip('my_garage', {
name: 'Premium Garage',
color: 5
});
// Move a blip and enable GPS route
exports['jinn-blips'].modifyBlip('police_checkpoint', {
coords: { x: 500.0, y: -1000.0, z: 32.0 },
route: true,
routeColor: 38
});Removes a blip from the map and deletes it from storage.
Syntax:
exports['jinn-blips']:removeBlip(id)
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
string |
✅ | The blip's unique ID |
Returns: boolean — true if removed, false if the ID wasn't found.
Lua Example:
-- Remove a specific blip
exports['jinn-blips']:removeBlip('my_garage')
-- Conditional removal
local removed = exports['jinn-blips']:removeBlip('police_checkpoint')
if removed then
print('Checkpoint blip removed successfully.')
endJavaScript Example:
exports['jinn-blips'].removeBlip('my_garage');
const removed = exports['jinn-blips'].removeBlip('police_checkpoint');
if (removed) {
console.log('Checkpoint blip removed successfully.');
}Toggles the GPS route line to a blip on the minimap/radar.
Syntax:
exports['jinn-blips']:setBlipRoute(id, enabled, routeColor)
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
string |
✅ | The blip's unique ID |
enabled |
boolean |
✅ | true to show route, false to hide it |
routeColor |
number |
❌ | Color ID for the route line |
Returns: boolean — true if the route was toggled successfully.
Lua Example:
-- Enable GPS route to a blip
exports['jinn-blips']:setBlipRoute('my_garage', true)
-- Enable GPS route with a custom color
exports['jinn-blips']:setBlipRoute('police_checkpoint', true, 38)
-- Disable GPS route
exports['jinn-blips']:setBlipRoute('my_garage', false)JavaScript Example:
// Enable GPS route
exports['jinn-blips'].setBlipRoute('my_garage', true);
// Enable GPS route with custom color
exports['jinn-blips'].setBlipRoute('police_checkpoint', true, 38);
// Disable GPS route
exports['jinn-blips'].setBlipRoute('my_garage', false);These exports can be called from any server-side script to interact with the permission and job systems.
Checks if a player has permission to access the blips dashboard.
Syntax:
exports['jinn-blips']:checkPermission(source)
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
source |
number |
✅ | The player's server ID |
Returns: boolean — true if the player is authorized.
Lua Example:
-- Check permission before performing an action
RegisterCommand('custom-blip-action', function(source)
if exports['jinn-blips']:checkPermission(source) then
-- Player is authorized, perform action
print('Player ' .. source .. ' is authorized for blip management.')
TriggerClientEvent('blip:openUI', source)
else
TriggerClientEvent('chat:addMessage', source, {
color = {255, 0, 0},
args = {'System', 'You do not have permission.'}
})
end
end, false)JavaScript Example:
// Check if a specific player has permission
RegisterCommand('custom-blip-action', (source) => {
const isAuthorized = exports['jinn-blips'].checkPermission(source);
if (isAuthorized) {
console.log(`Player ${source} is authorized.`);
TriggerClientEvent('blip:openUI', source);
} else {
TriggerClientEvent('chat:addMessage', source, {
color: [255, 0, 0],
args: ['System', 'You do not have permission.']
});
}
}, false);Returns the current job name of a player.
Syntax:
exports['jinn-blips']:getPlayerJob(source)
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
source |
number |
✅ | The player's server ID |
Returns: string — The player's job name (e.g. "police", "ambulance"), or "none" if not found.
Lua Example:
-- Get a player's current job
local job = exports['jinn-blips']:getPlayerJob(source)
print('Player job: ' .. job)
-- Use in a conditional
if exports['jinn-blips']:getPlayerJob(source) == 'police' then
-- Do something for police players
endJavaScript Example:
// Get a player's current job
const job = exports['jinn-blips'].getPlayerJob(source);
console.log(`Player job: ${job}`);
// Conditional usage
if (exports['jinn-blips'].getPlayerJob(source) === 'police') {
// Do something for police players
}| Event | Direction | Description |
|---|---|---|
blip:openUI |
Server → Client | Opens the blip dashboard for the player |
blip:loadSaved |
Server → Client | Sends saved blips data to the client on connect |
blip:setClientJob |
Server → Client | Updates the player's cached job name |
jinn-blips:client:setJob |
Client → Client | Internal event — bridge fires this when job changes |
| Event | Direction | Description |
|---|---|---|
blip:requestSaved |
Client → Server | Client requests saved blips on resource start |
blip:saveAll |
Client → Server | Client sends all blips to be persisted (admin only) |
-- In your dispatch resource (client-side)
-- Create a call blip that only police can see, flashes, and expires in 15 minutes
exports['jinn-blips']:createBlip(
'dispatch_' .. math.random(10000, 99999),
vector3(215.0, -810.0, 30.7),
161,
1,
'10-31 Robbery in Progress',
{
flash = true,
route = true,
job = 'police',
radius = 50,
opacity = 20,
expireAt = GetGameTimer() + (15 * 60 * 1000)
}
)-- On resource start, create blips for all your shops
local shops = {
{ id = 'shop_1', name = '24/7 Supermarket', coords = vector3(25.7, -1347.3, 29.5) },
{ id = 'shop_2', name = 'LTD Gasoline', coords = vector3(-47.0, -1757.5, 29.4) },
{ id = 'shop_3', name = 'Rob\'s Liquor', coords = vector3(-1222.9, -906.9, 12.3) },
}
for _, shop in ipairs(shops) do
exports['jinn-blips']:createBlip(
shop.id,
shop.coords,
52,
2,
shop.name,
{ scale = 0.8, shortRange = true }
)
end// In your custom admin resource (server-side)
// Only allow blip-authorized players to trigger a special action
onNet('myResource:requestSpecialAction', () => {
const src = global.source;
if (!exports['jinn-blips'].checkPermission(src)) {
emitNet('chat:addMessage', src, {
color: [255, 0, 0],
args: ['System', 'Access denied.']
});
return;
}
// Authorized — proceed with action
console.log(`Player ${src} triggered the special action.`);
});Blips are stored in data/blips.json in the following format:
[
{
"id": "mission_row_pd",
"name": "Mission Row Police",
"coords": { "x": -1127.24, "y": -846.27, "z": 19.32 },
"sprite": 137,
"color": 38,
"scale": 1,
"flash": false,
"route": false,
"job": "",
"radius": 0,
"showCenter": true,
"shape": "circle",
"opacity": 30,
"expireAt": null
}
]This resource is developed by Jinn. All rights reserved.
Made with ❤️ by Jinn — By C1 By P4S3 Development