Skip to content

Open-source TanStack packages

TanStack packages
by Amjad.

TypeScript packages for TanStack: typed query keys with scoped invalidation for TanStack Query, head tags, sitemaps, JSON-LD and OG images for TanStack Start, URL state with CSV and XLSX export for TanStack Table, Vercel AI SDK providers as TanStack AI adapters, and the server side of embedded Shopify apps on TanStack Start. Each one is MIT licensed and installed from npm.

5
Packages, all MIT
1
Support TanStack v5
01 Data fetching · MIT

Query Key Kit

@amjadiqbal/query-key-kit

TanStack Query v5's queryOptions() keeps one key next to its fetcher, but nothing says which keys exist, how they nest or which partial key to pass when you invalidate. Teams hand-write key arrays, or use @lukemorales/query-key-factory, which has had no release since February 2024 (1.3.4) and drops every option except queryKey and queryFn.

createQueryKeys(scope, schema) turns a plain object of entries into a key tree. Each entry gives back an object you can pass straight to useQuery, fetchQuery or useInfiniteQuery: the full queryKey, branded with the data type the way queryOptions() brands it, the queryFn, and every other option you wrote (staleTime, placeholderData, initialData, initialPageParam and so on). Nested contextQueries extend a parent key under _ctx. Every node of the tree carries its partial key, so invalidateScope, refetchScope, cancelScope, removeScope, getScopeData and setScopeData take the node instead of a hand-written array, and the data helpers know the type of every query they match. It keeps the function names and key output of @lukemorales/query-key-factory, with a few differences listed in the migration section. Tested with React in a real app and from a fresh npm install, and with Vue Query through type tests. An independent package, not made or endorsed by TanStack or by the author of @lukemorales/query-key-factory.

  • createQueryKeys builds a typed key tree from four entry kinds: null, a key tuple, an options object or a function returning either
  • Entries produce queryOptions()-shaped objects with a DataTag-branded queryKey, so getQueryData, setQueryData and fetchQuery return your data type without a cast
  • Every query option written on an entry is passed through unchanged, including staleTime, placeholderData, initialData and the infinite-query options
  • contextQueries nest child queries under a parent key, reached through _ctx
TanStack Query v5 v0.1.3 MIT
$ npm install @amjadiqbal/query-key-kit
02 SEO · MIT

Start SEO Kit

@amjadiqbal/start-seo-kit

TanStack Start gives every route a head() option and lets you add server routes, but the SEO itself is up to you. Each route ends up with a hand-written meta array where the canonical link, og:url and image URLs have to be made absolute by hand and kept in step with the title. Start's own sitemap support is tied to prerendering, so an app that renders pages on request has no sitemap.xml unless you write the XML yourself. JSON-LD is a loose object, and nothing tells you that a product's price has a currency symbol in it or that its rating has no count.

defineSeo() takes your site defaults once (URL, name, title template, default image, Twitter handle, locale, trailing-slash rule) and returns a seo() function for route head(). Each call returns { meta, links } in the shapes TanStack Router renders: the templated title, description, robots, a canonical link, hreflang alternates, Open Graph and article tags, Twitter card tags and JSON-LD, with every page and image URL made absolute. createSitemap() and createRobotsHandler() return GET handlers for Start server routes, so sitemap.xml and robots.txt are built per request and work when nothing is prerendered; past 50,000 URLs the sitemap turns into a sitemap index with numbered child files. The commerce entry point builds Product JSON-LD with Offer or AggregateOffer, ratings, reviews, shipping and return policy, the Open Graph product:* tags, and checkProduct(), which lists errors and warnings against Google's product documentation without throwing. The og entry point renders 1200x630 PNG share images with satori and resvg-wasm, which are optional peers. Tested with @tanstack/react-start 1.169.3 and @tanstack/react-router 1.170.45 (React) on Node in dev and in a production build, and from a fresh npm install. An independent package, not made or endorsed by TanStack.

  • defineSeo() and seo(): title templates, description, canonical, Open Graph, Twitter cards, robots, hreflang alternates, article tags and JSON-LD from one typed call in head()
  • Page and image URLs resolved against the site URL, with your router's trailing-slash rule applied to page URLs
  • createSitemap(): sitemap.xml from a Start server route, plain urlset up to 50,000 URLs, then a sitemap index of per-source child files; array and function sources load once per request, and paginated sources let each child sitemap load only its own range
  • routesFromTree() lists the parameter-free page routes of your route tree; entries support lastmod, changefreq, priority, hreflang alternates and images
TanStack Start (React) v0.1.1 MIT
$ npm install @amjadiqbal/start-seo-kit
03 Tables · MIT

Table URL State

@amjadiqbal/tanstack-table-url-state

TanStack Table v9 keeps its state in memory. Reload the page, share the link or press back, and the sorting, filters and page are gone. Wiring each slice to the search params by hand means writing a URL format, leaving defaults out, debouncing filter typing, choosing push or replace, and stopping Table's automatic return to page 1 from overwriting a page that came from a link. Exporting what the user filtered down to is a second job on top of that.

useTableUrlState() returns tableOptions to spread into useTable: the state and onChange handlers for sorting, column filters, global filter, pagination and column visibility. Table changes are written to the URL and URL changes (back/forward, links) are read back into the table. Params are short (sort=-total,customer, f.status=paid, q=acme, page=3, size=50, hide=email), values equal to the defaults are left out, filter typing waits 300 ms before it reaches the URL, and history entries are replaced unless you ask for push. Without a router it uses window.history and URLSearchParams; the /router entry adds a TanStack Router adapter that writes through router.navigate, plus a validateSearch helper. toCsv and downloadCsv export the filtered and sorted rows of every page in the visible data columns by default, with RFC 4180 quoting and formula escaping. The /xlsx entry writes a typed .xlsx file with its own small writer and fflate; load it with a dynamic import() so it stays out of the main bundle. Tested with React, TanStack Table v9 and TanStack Router 1.170 on Vite 8, in a real browser and from a fresh npm install. An independent package, not made or endorsed by TanStack.

  • useTableUrlState(): two-way sync of sorting, column filters, global filter, pagination and column visibility with the URL search params
  • Short, readable params (sort, q, f.<columnId>, page, size, hide); names can be renamed with keys or prefixed with prefix for two tables on one page
  • Values equal to the defaults are left out of the URL, a cleared non-empty default filter is written empty (q=, f.status=) so it stays cleared, and malformed params fall back to the defaults instead of throwing
  • Filter edits are debounced (300 ms by default); sorting, paging and visibility are written at once; replace by default, push on request
TanStack Table v9 (React) v0.1.1 MIT
$ npm install @amjadiqbal/tanstack-table-url-state
04 AI · MIT

AI SDK Bridge

@amjadiqbal/tanstack-ai-sdk-bridge

TanStack AI reaches a model through an adapter, and only some providers have one. The Vercel AI SDK has provider packages for many more, from Vercel and the community, but they implement the AI SDK provider spec, not TanStack AI's adapter classes. A provider without a TanStack adapter cannot be used with chat() or embed(), and moving an AI SDK app to TanStack AI means replacing every provider package at the same time as the server route and the client.

Six functions wrap an AI SDK model object as a TanStack AI adapter: aiSdkText for chat(), aiSdkEmbedding for embed(), aiSdkTranscription for generateTranscription(), aiSdkSpeech for generateSpeech(), aiSdkImage for generateImage() and aiSdkRerank for rerank(). The adapter calls the model's own doStream, doGenerate, doEmbed or doRerank method, so the ai package is not needed; @ai-sdk/provider is installed as a dependency and used only for its types. It reads the model's specificationVersion when the adapter is created and throws for anything other than v2, v3 or v4 (v3 or v4 for reranking). Chat streams text, reasoning and tool calls as TanStack AI events, so chat() runs your tools in its agent loop; structured output makes one separate, non-streamed call with a JSON response format. Provider-specific options and headers pass through modelOptions unchanged. Tested with real provider packages against a local mock of each provider's API (no live APIs, no keys) for DeepSeek, Anthropic, OpenAI, Together AI, Cerebras, Fireworks, ollama-ai-provider-v2, openai-compatible and Cohere rerank, in a real browser in a TanStack Start 1.169 app, and from a fresh npm install. An independent package, not made or endorsed by TanStack or Vercel.

  • aiSdkText: streaming chat with tool calls, tool choice, reasoning, structured output (outputSchema), token usage and abort
  • aiSdkEmbedding, aiSdkTranscription, aiSdkSpeech, aiSdkImage and aiSdkRerank for the other AI SDK model kinds
  • Reads AI SDK provider spec v2, v3 and v4 (AI SDK 5, 6 and 7 providers); reranking needs v3 or v4, and an unsupported spec throws when the adapter is created
  • modelOptions.providerOptions and modelOptions.headers go to the AI SDK call unchanged on every adapter; chat sampling settings (temperature, topP, topK, penalties, seed, stopSequences, maxOutputTokens) pass through by name
TanStack AI 1.x v0.1.1 MIT
$ npm install @amjadiqbal/tanstack-ai-sdk-bridge @tanstack/ai
05 Commerce · MIT

shopify-app-tanstack-start

@amjadiqbal/shopify-app-tanstack-start

Shopify publishes app packages for React Router, Remix and Express, but not for TanStack Start. A Start app has to copy session token validation, token exchange, webhook HMAC checks, app proxy signatures and billing redirects out of boilerplate repos, and then deal with the fact that Shopify's code throws Response objects the React Router way, which Start's server functions cannot send as they are.

shopifyApp() takes the same options as shopifyApp() in @shopify/shopify-app-react-router 4.0.0 and returns the same server API (authenticate.admin, authenticate.webhook, authenticate.public.appProxy, billing, unauthenticated.admin and the rest), because its server core is that package's MIT-licensed code with React Router removed. On top it adds the Start layer: middleware bodies for page requests, server functions, webhooks and app proxy routes, a handler for the /auth/$ route, a ready handler for the compliance webhooks, and conversion of the Responses Shopify's code throws into router redirects, App Bridge 401 responses or a readable ShopifyResponseError. A separate React entry loads App Bridge in the document head and routes App Bridge navigation through TanStack Router. Apps are embedded only, and installation is Shopify-managed with token exchange; there is no OAuth authorization code flow. Tested with @tanstack/react-start 1.169 against a simulated Shopify, in dev and a production build, with 408 tests and a fresh npm install. It has not been tested on a live Shopify store yet; see Compatibility. A community package, not made or endorsed by Shopify or by TanStack.

  • shopifyApp() with the server API of @shopify/shopify-app-react-router 4.0.0, adapted from Shopify's MIT-licensed code: authenticate.admin, webhook, flow, fulfillmentService, pos, public.appProxy, public.checkout and public.customerAccount, unauthenticated.admin and unauthenticated.storefront, registerWebhooks, login and addDocumentResponseHeaders
  • Embedded admin auth: session tokens from the id_token URL parameter on page loads and the Authorization: Bearer header on server function calls, the bounce and exit-iframe pages, token exchange for offline (and with useOnlineTokens, online) access tokens, the afterAuth hook, and the 401 retry and reauthorize headers App Bridge acts on
  • Webhooks verified by HMAC-SHA256 over the raw request body: 401 for an invalid HMAC, 400 for other invalid requests and for a signed body that is not JSON, 405 for anything but POST; the context includes an Admin API client when the shop has a stored offline session
  • complianceWebhooks() handles customers/data_request, customers/redact and shop/redact, plus app/uninstalled if pointed at the same route, and deletes the shop's stored sessions on shop/redact and app/uninstalled; when a handler or the session deletion fails it calls onError and answers 500, so Shopify retries, and still answers 500 if onError itself throws
TanStack Start (React) v0.1.2 MIT
$ npm install @amjadiqbal/shopify-app-tanstack-start

TanStack development

Need one of these built into your app, or something custom?

Integration, a custom package, or a whole TanStack feature, built and tested the same way as these. Tell me what you need and get a plan and a quote, free.

Need something built?

Free quote in 60 seconds