NX CreativeNX CreativeDocs
Scriptsnx_write

Exports

Public exports registered by nx_write.

nx_write exposes a client API for opening, placing, and closing writing sessions, plus a set of server-side helpers. The client exports let a custom inventory open, place, and close sessions, and let custom positioning tooling create placements directly. The server exports forward an open request to a player, create trusted placements, and read a saved document's preview image. RegisterPaperKind exists on both sides and lets another resource add its own writable paper at runtime. See Configuration for Config.Inventory, which decides whether nx_write touches ox_inventory itself or leaves item handling to you.

Client

exports.nx_write:placeFromSlotclient

Start the in-world placement preview for the note held in the given inventory slot. The slot's item must carry a saved document (a docId in its metadata); if it does not, the player is notified to save it first.

Parameters

  • slotnumber— ox_inventory slot number of the notepad or post-it to place.

Returns

nil
-- Wired on the notepad item in ox_inventory/data/items.lua,
-- so the inventory's right-click "Place" button enters placement.
buttons = {
    {
        label = 'Place',
        action = function(slot)
            exports.nx_write:placeFromSlot(slot)
        end,
    },
},

During the preview the player aims at a surface, then commits with the Config.Placement.controls.commit key or cancels with the cancel key. On commit the note is placed for every nearby player and the item is consumed from the slot.

exports.nx_write:Openclient

Open or continue a writing session. Returns false if a session or placement preview is already active, or if the kind is unknown. Pass docId to continue a saved document; omit it for a new one. slot and itemName are opaque values echoed back in nx_write:documentSaved on save.

Parameters

  • optstable— { kind?: string (default "notepad"), docId?: number, title?: string, slot?: any, itemName?: string, page?: number, readOnly?: boolean, background?: { url?: string, dataUrl?: string } }.

Returns

boolean
-- Continue a document your custom inventory stored on the item.
exports.nx_write:Open({
    kind     = 'notepad',
    docId    = item.metadata.docId,
    slot     = item.slot,
    itemName = item.name,
})
exports.nx_write:RegisterPaperKindclient

Register a writable paper kind at runtime, so another resource can add its own paper without editing nx_write. The def needs a kind key plus whatever differs from the template. Everything else is filled in from a small-card default, and the session feel (hold pose, pen, write animation, hand IK) is inherited from the live post-it entry, so a registered kind tracks future post-it retunes instead of a frozen copy. Call the server export with the same def, or saving a session of that kind is rejected. Built-in kinds cannot be overwritten. Returns true, or false plus a reason string.

Parameters

  • deftable— { kind: string (required, unique), label?: string, prop?: string, panelWidth?: number, panelHeight?: number, multiPage?: boolean, features?: { tear?: boolean, fontButton?: boolean }, background?: { box: { x, y, w, h } }, ...any other paper field you want to override }.

Returns

boolean, string?
-- Register on both sides, then open sessions with your own item.
exports.nx_write:RegisterPaperKind({
    kind       = 'polaroid',
    label      = 'Polaroid',
    prop       = 'prop_cs_polaroid',
    features   = { tear = false, fontButton = false },
    background = { box = { x = 0.06, y = 0.05, w = 0.88, h = 0.72 } },
})

exports.nx_write:Open({
    kind       = 'polaroid',
    docId      = item.metadata.docId,
    slot       = item.slot,
    itemName   = item.name,
    background = { url = photoUrl },
})

[!note] A registered kind gets no in-world placement entry and no ox_inventory item. In-world placement and item handling stay with the host resource: open sessions with your own item's slot and itemName, then persist the docId from nx_write:documentSaved. features hides DUI buttons that do not apply to your paper, and background.box is the page-fraction box the per-document background image renders into.

exports.nx_write:Placeclient

Start the in-world placement preview for a saved document. docId is required, since the placement renders the document's saved preview image. slot and itemName ride along to the server and are echoed on nx_write:placementCreated.

Parameters

  • optstable— { kind?: string (default "notepad"), docId: number, slot?: any, itemName?: string }.

Returns

boolean
exports.nx_write:Place({ kind = 'notepad', docId = item.metadata.docId, slot = item.slot, itemName = item.name })
exports.nx_write:CreatePlacementclient

Create a committed placement at explicit coords and rotation, skipping the in-world aim preview. This is the hook for custom positioning tooling (transform gizmos, snapping, free-rotate): your UX owns where the note goes, nx_write owns persistence, render, and multiplayer sync. It runs the same server callback as a preview commit, so it persists, bakes and renders on every client, and fires nx_write:placementCreated. Returns the placement id on success, or false. A client-driven create is still validated against the player's position (Config.Placement.maxPlaceDistance plus 2 m slack); use the server export for an unbounded, trusted placement.

Parameters

  • optstable— Pose, in priority order. { kind?: string (default "notepad"), docId: number (the saved document to render), entity?: number (read coords and quat off a live entity, the precise path: point it at your gizmo preview prop), coords?: vector3, quat?: { x, y, z, w } (lossless, preferred over rotation), rotation?: vector3 (pitch, roll, yaw in GTA euler degrees), surfaceKind?: "wall" | "table" (inferred from the pose when omitted), slot?: any, itemName?: string }.

Returns

number | false
-- From a transform gizmo: the tool owns positioning, nx_write owns the rest.
-- Passing a live entity is the precise path: the placement is
-- byte-identical to the preview prop the player was dragging.
local id = exports.nx_write:CreatePlacement({
    kind     = 'notepad',
    docId    = item.metadata.docId,
    entity   = gizmoPreviewProp,
    slot     = item.slot,
    itemName = item.name,
})

-- Or place at explicit coords with a lossless quat.
exports.nx_write:CreatePlacement({
    kind   = 'notepad',
    docId  = item.metadata.docId,
    coords = vec3(215.7, -810.2, 30.7),
    quat   = { x = 0.0, y = 0.0, z = 0.0, w = 1.0 },
})

[!note] Prefer entity or quat for the pose. Both are exact. rotation (GTA euler) is a convenience that round-trips cleanly for flat or upright poses only. When surfaceKind is omitted, nx_write infers it from the prop's world face normal and validates it against the kind's allowed surfaces.

exports.nx_write:RefreshPlacementsclient

Re-pull placements from the server and render any not held locally. Live creates already arrive over the spawn broadcast, so this is a manual resync for tooling that writes to nx_write_placements out of band. Idempotent. Returns true if the refresh ran.

Returns

boolean
exports.nx_write:RefreshPlacements()
exports.nx_write:Closeclient

Save (or discard) the active writing session and close it. Pass true to discard without saving. Returns false if no session was active.

Parameters

  • discardboolean— true discards the session without saving. Defaults to false (save and close).

Returns

boolean
exports.nx_write:Close()      -- save and close
exports.nx_write:Close(true)  -- discard and close
exports.nx_write:IsBusyclient

True while a writing session or a placement preview is active. Gate your own item-use with it so a second use does not double-open.

Returns

boolean
if not exports.nx_write:IsBusy() then
    exports.nx_write:Open({ kind = 'notepad', docId = item.metadata.docId })
end

Server

exports.nx_write:Openserver

Forward an open request to a player's client. Mirrors the client Open export and accepts the same opts. Use this when your inventory routes item-use through a server hook.

Parameters

  • sourcenumber— Server id of the player to open the session for.
  • optstable— Same shape as the client Open export.

Returns

boolean
-- From your inventory's server-side item-use handler.
exports.nx_write:Open(source, {
    kind     = 'notepad',
    docId    = item.metadata.docId,
    slot     = item.slot,
    itemName = item.name,
})
exports.nx_write:RegisterPaperKindserver

Server-side twin of the client RegisterPaperKind export. Pass the same def. Saving validates the payload kind against the registered papers, so a kind registered only on the client would open sessions it can never save. Returns true, or false plus a reason string.

Parameters

  • deftable— Same shape as the client RegisterPaperKind export.

Returns

boolean, string?
exports.nx_write:RegisterPaperKind({ kind = 'polaroid', label = 'Polaroid' })
exports.nx_write:GetDocumentPreviewserver

Read a saved document's stored preview image, so a host resource can render the ink outside nx_write (on its own prop, in its own UI). Returns nil if the document does not exist. The preview is a transparent PNG data URL. The caller owns its own access policy: serve only the kinds you registered, or private notepad ink becomes readable.

Parameters

  • docIdnumber— Document id, as received from nx_write:documentSaved.

Returns

table | nil
-- { kind = 'polaroid', title = 'Beach', dataUrl = 'data:image/png;base64,...' }
local preview = exports.nx_write:GetDocumentPreview(docId)
if preview and preview.kind == 'polaroid' then
    TriggerClientEvent('myresource:drawInk', src, preview.dataUrl)
end
exports.nx_write:CreatePlacementserver

Create a placement at explicit coords from the server, bypassing the aim preview. The trusted mirror of the client CreatePlacement export. Because the server supplies the coords, the per-player rate-limit and distance gate are skipped (those guard the untrusted client callback). Everything else is identical to a preview commit: it persists, broadcasts so every client bakes and renders it, and fires nx_write:placementCreated. Pass coords with quat or rotation; there is no entity option, since the server cannot read a client entity. Returns the placement id on success, or false.

Parameters

  • sourcenumber— Server id of the player credited with the placement.
  • optstable— { kind?: string (default "notepad"), docId: number, coords: vector3, quat?: { x, y, z, w } (lossless, preferred), rotation?: vector3 (pitch, roll, yaw in GTA euler degrees), surfaceKind?: "wall" | "table" (defaults to a surface the kind allows), slot?: any, itemName?: string }.

Returns

number | false
-- Seed a placement programmatically from a server hook.
local id = exports.nx_write:CreatePlacement(source, {
    kind   = 'notepad',
    docId  = docId,
    coords = vec3(215.7, -810.2, 30.7),
    quat   = { x = 0.0, y = 0.0, z = 0.0, w = 1.0 },
})

[!note] These exports work in any inventory mode. They pair with the server events (nx_write:documentSaved, nx_write:placementCreated, nx_write:placementPickedUp), which is how a custom inventory persists the docId and bridges items on place and pickup.

On this page

NX Docs