@choksheak/react-utils

Chok’s React Utilities

Fetch data in React using Shared Queries:

import { sharedQuery, useSharedQuery } from "@choksheak/react-utils/sharedQuery";

export const userQuery = sharedQuery<{ id: string }, User>({
  url: "/api/user",
});

export const UserGreeting: React.FC<{ userId: string }> = ({ userId }) => {
  // The query url becomes `/api/user?id=${userId}`.
  const { data, error, loading } = useSharedQuery(userQuery, { id: userId })
 
  if (loading) return "Loading...";
  if (error) return String(error);
  return `Hello ${data?.name}`;
};

This package is a time-tested collection of React utilities written in TypeScript that I have personally needed in almost every project that I have worked in. Making this an open source, public npm package allows me to share this code freely with others as well as using it for my own personal and professional projects. Feel free to use it in your own projects without any restrictions.

The code supports full tree-shaking. Therefore your code will only grow in size by whatever you use. Any code that you are not using will not contribute to your code size.

πŸš€ @choksheak/react-utils allows you to reuse common React+Typescript functions without reinventing the wheels, thus saving you time and reducing the size of your code.

Why Use This? ↑

This package is built after similar open source packages like TanStack Query and SWR existed. Why should you use this package instead of the others? This package offers the following features which could potentially be built into the other existing libraries, but requires a fair bit of code and effort:

This means that if you use this package, as compared to the other packages, it should always save you both time and effort, and provide your code with better performance, with writing the minimum amount of code needed to define what you actually want, with no boilerplate code needed. The code you produce using this library is almost always more powerful, more concise and more performant than the code you could produce using any other library that exists today (December 2025).

Please see the examples below and compare that to how much more difficult it would be to produce the same functionality using any other library.

Get Started ↑

Install the package using the standard package manager command:

npm i @choksheak/react-utils

Examples ↑

fetcher.ts

import { fetcher } from "@choksheak/react-utils/fetcher";

// Fetch some data from the server, with auto-retries.
const data = await fetcher.url("/api/get-user-data").get();

// Print data which should be a json object.
console.log(`Fetched data:`, data);

NoSSR.ts

"use client";

import { NoSSR } from "@choksheak/react-utils/NoSSR";

export const ClientSideOnlyComponent: React.FC = () => {
  // Ensure that the component only gets rendered on the client side.
  return (
    <NoSSR>
      <ComponentThatOnlyRendersOnTheClientSide />
    </NoSSR>
  );
};

sharedQuery.ts

"use client";

import { fetcher } from "@choksheak/react-utils/fetcher";
import { sharedQuery, useSharedQuery } from "@choksheak/react-utils/sharedQuery";
import { MS_PER_HOUR } from "@choksheak/ts-utils/timeConstants";

// Create a shared query object that is reusable in other parts of the code.
export const listUsersQuery = sharedQuery({
  url: "/api/list-users",
});

// Similar to listUsersQuery, but adding persistence.
export const listUsersQuery2 = sharedQuery({
  // Specify `queryName` to avoid conflict with listUsersQuery.
  queryName: "listUsers2", 
  url: "/api/list-users",
  persistTo: "indexedDb",
  staleMs: MS_PER_HOUR,
});

// Similar to listUsersQuery, but using a custom queryFn instead of url.
export const listUsersQuery3 = sharedQuery({
  // `queryName` is required when using queryFn.
  queryName: "listUsers3",
  queryFn: (): Promise<User[]> => fetcher.url("/api/list-users").get(),
});

export const ListUsersComponent1: React.FC = () => {
  // Use the shared query to get data.
  // Note that while you can also use the `const { data ... } =` syntax, it
  // does not scale well when you need multiple shared queries in the same
  // component, so we recommend using the `const listUsers =` syntax instead.
  const listUsers = useSharedQuery(listUsersQuery);

  return (
    <>
      <h1>Users</h1>

      {listUsers.loading && "Loading..."}

      {!listUsers.data && listUsers.error ? String(listUsers.error) : null}

      {listUsers.data?.map((user, index) => {
        return <User key={index} user={user} />
      }) ?? "-"}
    </>
  );
};

export const ListUsersComponent2: React.FC = () => {
  // Example using the unpack assignment syntax.
  // Suitable when you only have one shared query in a component.
  const { loading, data, error } = useSharedQuery(listUsersQuery);

  return (
    <>
      <h1>Users</h1>

      {loading && "Loading..."}

      {!data && error ? String(error) : null}

      {data?.map((user, index) => {
        return <User key={index} user={user} />
      }) ?? "-"}
    </>
  );
};

sharedState.tsx

"use client";

import { sharedState } from "@choksheak/react-utils/sharedState";

// Create a shared state that can be reused anywhere in the code.
export const searchTermState = sharedState(
  "", { store: { persistTo: "indexedDb", key: "searchTerm" }}
);

export const SearchTermInput: React.FC = () => {
  // Think of `useSharedState` as like `useState`.
  const [searchTerm, setSearchTerm] = useSharedState(searchTermState);

  return (
    <>
      Search term:{" "}
      <input
        type="text"
        value={searchTerm}
        onChange={(e) => setSearchTerm(e.target.value)}
      />
    </>
  );
};

Reference ↑

For more details about each module, please refer to the Reference Documentation