npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

nuikit-core

v0.9.0

Published

The runtime behind nuikit, one design system for your whole FiveM server: typed fetchNui and events, surfaces (focus, layers, Escape across resources), the theme compiler (tokens v1, DTCG) and browser devtools.

Downloads

2,265

Readme

nuikit-core

npm CI MIT

Part of nuikit: shadcn/ui and Tailwind v4 for FiveM NUI, rendered right. Website & live demo →

Typed, framework-agnostic NUI runtime for FiveM, plus the Lua half (lua/nuikit.lua).

npm i nuikit-core

Types, once

// nui.d.ts
declare module "nuikit-core" {
  interface NuiEvents {    // Lua → UI  (NUI.send)
    "hud:vitals": { health: number; armor: number };
  }
  interface NuiCallbacks { // UI → Lua  (fetchNui ↔ NUI.on)
    "shop:buy": { req: { id: string }; res: { ok: boolean } };
  }
}

Vanilla

import { fetchNui, onNui, closeNui, isBrowser, mockNui } from "nuikit-core";

onNui("hud:vitals", (v) => render(v));
const { ok } = await fetchNui("shop:buy", { id: "zentorno" });
mockNui("shop:buy", ({ id }) => ({ ok: id !== "sold-out" })); // browser only

React

import { useNuiEvent, useNuiState, useNuiVisibility, useNuiCallback } from "nuikit-core/react";

const vitals = useNuiState("hud:vitals", { health: 100, armor: 0 });
const { visible, close } = useNuiVisibility(); // NUI.open/close + Escape
const buy = useNuiCallback("shop:buy");       // { call, loading, error, data }

Surfaces

Declare each piece of UI in Lua; nuikit decides focus, cursor, stacking and Escape across every resource that uses it.

local inv = NUI.surface('inventory', { layer = 'panel', exclusive = true, hides = { 'hud' } })
inv:open({ items = GetItems() })
NUI.state('dead', true) -- surfaces with blockedBy = { 'dead' } refuse to open
import { Surface, useSurface } from "nuikit-core/react";

<Surface name="inventory" layer="panel" hides={["hud"]}>
  {(data) => <InventoryGrid items={data.items} />}
</Surface>;

const inv = useSurface("inventory"); // { open, visible, focused, data, close }

Layers stack hud < world < panel < modal < system. Focus goes to the top surface that takes input; Escape closes top-down. Type surface data with interface NuiSurfaces next to your events.

Live values

A value that moves every frame (a compass, a speedometer) streams from Lua at ≤30 messages a second, only when it changed, and the page glides between them at the game's frame rate without rendering (protocol §5.7).

local rpm = NUI.stream('hud:rpm', { rate = 30, precision = 0.01 }) -- kind = 'angle' for headings
rpm:push(GetVehicleCurrentRpm(vehicle)) -- from any loop; it decides when to send
import { useFollow } from "nuikit-core/react"; // or follow() from "nuikit-core", without React

const bar = useRef<HTMLDivElement>(null);
useFollow("hud:rpm", (v) => { bar.current!.style.transform = `scaleX(${v})`; }, { start: 0 });

In FiveM requestAnimationFrame runs at the game's frame rate (140–300/s): never send a message or a callback per frame.

Devtools

import { NuiDevtools, defineMocks } from "nuikit-core/devtools";

const mocks = defineMocks({
  events: { "hud:vitals": [{ health: 100, armor: 50 }, { health: 12, armor: 0 }] },
  callbacks: { "shop:buy": () => ({ ok: true }) },
});

<NuiDevtools {...mocks} />; // press ` · renders nothing in game

Runtime theming

import { applyTheme } from "nuikit-core";
applyTheme(pastedTweakcnCss); // sets vars + Chromium 103 rgb channels

Lua

Copy lua/nuikit.lua into your resource (create-nuikit does it for you) and list it in client_scripts before your own files.

NUI.send('hud:vitals', { health = 80 })
NUI.on('shop:buy', function(data) return { ok = true } end)
local menu = NUI.surface('menu', { layer = 'panel' })
menu:open(data) menu:close()
NUI.state('dead', true)
NUI.open({ cursor = true, keepInput = false }) -- single-UI mode
NUI.close()
NUI.notify({ title = 'Paid', type = 'success' })
NUI.stream('hud:rpm', { rate = 30 }):push(0.62) -- a live value (see above)

Set setr nuikit_debug 1 to log traffic.


MIT · Built by @belibathan · github.com/belibathan/nuikit