
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
@void-snippets/client
Framework-agnostic generic CRUD resource service
- ResourceService<…> CRUD
- Axios-powered, zero deps
- handleApiError() normalizer
@void-snippets/react
TanStack Query, Socket.IO, routing, and React hooks
- createResourceHooks() factory
- createSocketHooks() factory
- createRouteContract() factory
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-routerArchitecture
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.
// 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'] }),
});// 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();