Presentation block (present)
Stability: Stable.
present is the one shared block every descriptor — a mode, a
cosmetic pack, or a battleground — uses to present
itself. It holds the whole card identity (title / sub / rgb) plus the showcase
(desc / hero) and external info / buy links. One shape, one validator, shared across
every domain — so a mode and a pack present identically instead of each re-inventing its
own name / sub / rgb fields.
It is pure presentation — it never holds functional data (a mode keeps its flow/rules,
a pack its status/sku/skins outside present). Every field is optional, so a
domain may still default a missing one.
Fields
| Field | Type | Notes |
|---|---|---|
title | string | Display title — the card heading. (A mode also keeps a functional label.) |
sub | string | Short subtitle under the title (e.g. Solo · Best of, VIP). |
rgb | string | "r g b" accent triplet for the card (CSS rgb(var(--m-rgb))). |
desc | string | One-paragraph description — the hero sub-line / pack blurb. |
hero | string | Hero / splash-art image URL — the card banner. A CDN http(s)://…, a /path, or a cross-resource nui://<resource>/<path> for art bundled in the descriptor's own resource. |
card | string | Thumbnail for a grid cell — same URL forms as hero, authored small (16:9, ~640×360). Used by pickers that draw many cells at once, such as the map picker. A surface that wants a thumb falls back to hero when card is absent. |
info | string | "More info" external link URL (docs, a teaser page, …). |
buy | string | "Buy" external link URL. A cosmetic pack also derives one from its sku. |
All fields are optional strings. info/buy should look like a web URL
(http(s)://… or /path); hero and card may additionally be a nui://… asset (see
Hosting the art). A value that doesn't match is a load-time
warning, not an error.
Why
cardis separate fromhero. A picker decodes every visible thumbnail when the menu opens. Feeding it full-bleed 1920×1080 splash art is a real frame-time cost in CEF, so ship a small one for grids and keepherofor the full-width band.
Hosting the art
hero is loaded straight into an <img> in the NUI, so it must be a URL the page can
fetch — pick one of:
-
CDN / website — an absolute
http(s)://…URL. Best when the art is large, shared, or updated independently of the resource. -
Bundled in your resource — ship the file in your resource's
media/and reference it cross-resource asnui://<resource>/<path>. Declare the file in your fxmanifest'sfiles{}so clients download it (a server-only mode needs noclient_scriptsfor this). Self-contained — the cover travels with the mode. This is what the shippedmode_duelreference does:-- fxmanifest.lua files { 'media/*.webp' } -- config.lua present = { hero = 'nui://mode_duel/media/cover.webp' }
Links open via copy-to-clipboard. A FiveM NUI page can't open the system browser, so the menu copies an
info/buyURL and tells the player to paste it — the same pattern as the Discord button. Host the target anywhere (your site, a Tebex page).
Hero art dimensions — the two surfaces differ.
herois always rendered with CSSobject-fit: cover(scaled to fill its frame and cropped), but the frame's shape depends on which descriptor ships it, so author for the right one:
Descriptor Frame Author Crop note Mode hero band — wide & short landscape 16:9 (1920×1080, min 1600×900) the band darkens the leading (left) edge for the overlaid copy — keep the focal point off the far-left third Cosmetic pack unlock panel — a tall left column portrait ~3:4 (e.g. 900×1200, min 750×1000) fills a vertical column edge-to-edge — subject CENTRED, full-height composition All
.webp. A 16:9 image in the pack panel (or a portrait in the mode band) survives but crops hard to its centre strip, so match the surface.
Example
present = {
title = 'Neon',
sub = 'Premium',
rgb = '245 191 79',
desc = 'A premium streamed skin pack — yours forever.',
hero = 'https://cdn.example.com/packs/neon-hero.webp',
info = 'https://example.com/packs/neon',
buy = 'https://example.com/store/package/neon',
}Where it applies
The engine (modes) and the customization module (packs) flatten present onto the
top-level name/sub/rgb/desc fields the NUI reads, and also pass the block through for
the hero band — so both surfaces present from the same source.
| Descriptor | Surfaced by |
|---|---|
| Cosmetic pack | The Appearance rail (title/sub/rgb as the pack tab) + unlock panel (hero as art, desc as the blurb, info as a "More info" link; the Buy CTA derives from sku). |
| Mode | Forwarded in the lobby snapshot; the rail card uses title/sub/rgb, and the mode cover renders the identity band: hero as full-bleed splash art (falling back to the typographic band when absent), title as the page title, desc as the sub-line, info/buy as the cover's "Preview" link row (copy-to-clipboard in-game). |
| Battleground | Carried for a future map-picker surface — no player-facing battleground card renders it yet. |
Authoring
present is validated by the shared SDK helper (Sdk.validatePresent, @sdk/shared/kit.lua),
which each domain's defineMode / definePack / defineBattleground calls — so a malformed
block fails loudly at your resource's own load, like the rest of the descriptor.