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

@trekscripts/core

v0.1.9

Published

React query for FiveM NUI (Native UI) communication. Drop these into any React-based NUI resource to send requests and listen for game events.

Readme

@trekscripts/core — Core

React query for FiveM NUI (Native UI) communication. Drop these into any React-based NUI resource to send requests and listen for game events.

Install

pnpm add @trekscripts/core

Peer dependencies: react >= 18.

query

useNUIQuery

Sends HTTP requests to your FiveM resource and manages the response in state. Generic over the response and body types for full type safety.

import { useNUIQuery } from "@trekscripts/core/query";

interface Player {
  id: number;
  name: string;
}

function PlayerList() {
  const { data, loading, error, refetch } = useNUIQuery<{ players: Player[] }>("getPlayers");

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error.message}</p>;

  return (
    <ul>
      {data?.players.map((p) => (
        <li key={p.id}>{p.name}</li>
      ))}
    </ul>
  );
}

Signature

useNUIQuery<TData = unknown, TBody = unknown>(
  endpoint: string,
  options?: NUIQueryOptions<TBody>,
): NUIQueryResult<TData>

Options

| Property | Type | Default | Description | | --------- | --------- | ------- | ---------------------------------------------------------------------------------------------------- | | body | TBody | {} | JSON body sent with the POST request. | | enabled | boolean | true | When false, the query is disabled and will not fetch on mount. Setting to true triggers a fetch. |

Return value

| Property | Type | Description | | --------- | --------------------------- | --------------------------------------------------------------------------- | | data | TData \| null | Response body, or null if not yet fetched. | | loading | boolean | true while the request is in flight. | | error | Error \| null | Non-null when the request fails. | | refetch | (opts?) => Promise<TData> | Re-run the request. Pass an options override or omit to reuse the last one. | | query | (opts?) => Promise<TData> | Alias for refetch. |

Enabled vs disabled

// Enabled (default) — fetches automatically on mount.
const { data } = useNUIQuery("getConfig");

// Disabled — only fetches when refetch/query is called or enabled becomes true.
const { refetch } = useNUIQuery("getConfig", { enabled: false });

Conditional fetching

// Fetch only when playerId is available
const { data } = useNUIQuery("getPlayer", {
  enabled: !!playerId,
  queryKey: `player-${playerId}`,
  staleTime: 5000,
});

Passing a typed body

const { data } = useNUIQuery<PlayerStats, { id: number }>("getPlayerStats", {
  enabled: true,
  body: { id: 1 },
});

Incoming NUI messages

useNUIQuery listens for message events where either the type or action field matches the endpoint name. This covers the two common FiveM conventions:

// Either of these will update `data`:
SendNUIMessage({ type: "playerData", data: { id: 1 } });
SendNUIMessage({ action: "playerData", data: { id: 1 } });

const { data } = useNUIQuery("playerData");
// data → { id: 1 }

Non-object payloads (strings, numbers, null) are safely ignored without throwing.

Backward compatibility

useNUI is exported as an alias for useNUIQuery. Existing code using useNUI continues to work without changes.

import { useNUI } from "@trekscripts/core/query";
// useNUI is identical to useNUIQuery

useNUIMutation

Mutation hook for sending write operations to NUI. Follows TanStack Query patterns with mutate (fire-and-forget) and mutateAsync (returns Promise). Supports lifecycle callbacks and type-safe generics.

import { useNUIMutation } from "@trekscripts/core/query";

function CreatePlayer() {
  const { mutate, mutateAsync, isPending, isSuccess, error, reset } = useNUIMutation<
    Player,
    { name: string }
  >("createPlayer", {
    onSuccess: (data) => console.log("Created:", data.id),
  });

  return (
    <form
      onSubmit={(e) => {
        e.preventDefault();
        mutate({ name: "Trek" });
      }}
    >
      <button disabled={isPending}>{isPending ? "Creating..." : "Create Player"}</button>
      {isSuccess && <p>Player created.</p>}
      {error && <p>Error: {error.message}</p>}
      <button type="button" onClick={reset}>
        Reset
      </button>
    </form>
  );
}

Signature

useNUIMutation<TData, TBody, TError = Error>(
  endpoint: string,
  options?: NUIMutationOptions<TData, TBody, TError>,
): NUIMutationResult<TData, TBody, TError>

Options

| Property | Type | Description | | ----------- | ------------------------------------------------------------------------ | ----------------------------------- | | onSuccess | (data: TData, variables: TBody) => void | Called after a successful mutation. | | onError | (error: TError, variables: TBody) => void | Called when the mutation fails. | | onSettled | (data: TData \| null, error: TError \| null, variables: TBody) => void | Called after success or failure. |

These callbacks are also available per-call on mutate and mutateAsync.

Return value

| Property | Type | Description | | ------------- | --------------------------------------------- | ----------------------------------------------------- | | data | TData \| null | Result of the last successful mutation. | | error | TError \| null | Error from the last failed mutation. | | isPending | boolean | true while a mutation is in flight. | | isIdle | boolean | true before any mutation has been executed. | | isSuccess | boolean | true after a successful mutation. | | isError | boolean | true after a failed mutation. | | mutate | (variables, callOptions?) => void | Fire-and-forget. Catches internally, does not throw. | | mutateAsync | (variables, callOptions?) => Promise<TData> | Returns a Promise. Throws on failure. | | reset | () => void | Restores data, error to null and state to idle. |

mutate vs mutateAsync

// mutate — fire-and-forget, errors are caught internally
mutate(
  { name: "Trek" },
  {
    onSuccess: (data) => console.log(data),
    onError: (err) => console.error(err),
  },
);

// mutateAsync — returns Promise, caller handles rejection
try {
  const result = await mutateAsync({ name: "Trek" });
} catch (err) {
  // handle error
}

Lifecycle callbacks

Callbacks fire in order: onSuccess/onError first, then onSettled. Hook-level options are called first, followed by per-call options.

const { mutate } = useNUIMutation("endpoint", {
  onSuccess: (data, vars) => {
    // fires after every successful mutation
  },
  onSettled: (data, error, vars) => {
    // fires after both success and failure
  },
});

mutate(payload, {
  onSuccess: (data, vars) => {
    // fires only for this specific call
  },
});

useNUIEvent

Listens for NUI message events dispatched from the game. Only the handler matching the given action string runs.

import { useNUIEvent } from "@trekscripts/core/query";

function HealthBar() {
  useNUIEvent<{ health: number }>("updateHealth", (data) => {
    setHealth(data.health);
  });

  return <div>{health}%</div>;
}

Signature

useNUIEvent<T = any>(action: string, handler: (data: T) => void): void

| Parameter | Description | | --------- | ------------------------------------------------------------------------------------------- | | action | The action field to match on incoming message events. | | handler | Callback receiving the data payload. Always the latest version (no stale closure issues). |

Non-object payloads are safely ignored without throwing.

Event shape expected from FiveM

{
  "action": "updateHealth",
  "data": { "health": 75 }
}

FiveM integration

useNUIQuery and useNUIMutation resolve the resource name via GetParentResourceName() automatically. In a real FiveM NUI context this returns the correct name; in tests or standalone React apps it falls back to "nui-res-name".

The fetch URL follows the standard pattern:

https://{resourceName}/{endpoint}

Running tests

pnpm test

Uses Vitest with jsdom. The test setup stubs window.GetParentResourceName and exposes a dispatchNUIEvent helper for simulating game events.