built lazily by@shahtirthhh
Void Snippets

Snippets to make your boilerplate void

Branded IDs, adapter-first CRUD, and React hook factories — zero boilerplate, strict TypeScript throughout.

Three packages

One philosophy

Independently installable — composable when you need the full stack.

@void-snippets/core

Shared types, branded IDs, adapters, and utilities

  • VSId<K,T> branded types
  • catchError() Go-style tuples
  • VSAdapters + defaults
v0.3.0

@void-snippets/client

Framework-agnostic generic CRUD resource service

  • ResourceService<…> CRUD
  • Axios-powered, zero deps
  • handleApiError() normalizer
v0.3.0

@void-snippets/react

TanStack Query, Socket.IO, routing, and React hooks

  • createResourceHooks() factory
  • createSocketHooks() factory
  • createRouteContract() factory
v0.6.0

Get started

Install in seconds

Add the packages you need, plus axios and TanStack Query for the React hook factories.

$ npm install axios @tanstack/react-query
# optional: socket.io-client, react-router

Architecture

Monorepo structure

Core has zero runtime deps. Client works in Node or plain TS. React adds TanStack Query, Socket.IO, and type-safe routing.

Before & after

void-snippets in practice

Define a resource once. Get fully-typed list, get, mutations, and infinite hooks — with optional optimistic updates built in.

contacts.hooks.ts
// Manual TanStack Query setup per resource
const { data, isLoading, refetch } = useQuery({
  queryKey: ['contacts', params],
  queryFn: () => ContactsApis.list(params),
});

const createMutation = useMutation({
  mutationFn: ContactsApis.create,
  onSuccess: () => queryClient.invalidateQueries({ queryKey: ['contacts'] }),
});

const updateMutation = useMutation({
  mutationFn: ({ id, payload }) => ContactsApis.update(id, payload),
  onSuccess: () => queryClient.invalidateQueries({ queryKey: ['contacts'] }),
});
contacts.hooks.ts
// One factory — fully typed CRUD hooks
export const contactHooks = createResourceHooks('contacts', ContactsApis, {
  defaultParams: { page: 1, limit: 20 },
  optimistic: {
    update: (cache, { _id, payload }) =>
      cache.map(item => item._id === _id ? { ...item, ...payload } : item),
    remove: (cache, id) => cache.filter(item => item._id !== id),
  },
});

const { list, isLoading, refetch } = contactHooks.useList({ page: 1, limit: 20 });
const { create, update, remove } = contactHooks.useMutations();

Open source

Contributors

Built in public. Everyone is welcome to contribute.

shahtirthhh

shahtirthhh

Creator & Maintainer

You?

Contributor

Contribute ↗