Skip to content
qt

How it works

From mutation to UI update

A server action fires. Six steps later, every subscribed browser tab shows fresh data. Here's exactly how it flows.

The big picture

End-to-end data flow

Scroll to reveal each stage. The entire pipeline happens in milliseconds.

1Server MutationcreateServerFn()
2invalidateTags()Tag dispatch
3Tag ResolutionGroup to children
4SSE BroadcastEventSource push
5Client ReceivesEvent handler
6Tag MatchingActive queries
7Query RefetchqueryClient
8React Re-renderFresh data

Step by step

Six steps explained

Click any step to expand the explanation and code example.

What's happening

The tag tree is your contract between server and client. Parent nodes are group tags — calling appTags.todos() returns a tag that matches all todos.* queries. Leaf nodes return specific tags for single-item precision. TypeScript infers the full tree.

Code

import { function defineTags<T extends ValidTagFactoryTree<T>>(factories: T): ResolvedTagTree<T>
Builds a typed, callable tag tree from a plain factory object. Each leaf becomes a tag factory stamped with its `TAG_PATH`; each branch becomes a callable `TagGroup` that expands to all descendant leaves when invoked without arguments.
@paramfactories - Nested object of tag factory functions.@returnsA fully-typed tag tree with autocomplete and compile-time safety.@example```ts const appTags = defineTags({ todos: { list: () => ["todos"], byId: (id: string) => ["todo", id] } }); appTags.todos.list(); // leaf tag value appTags.todos(); // TagGroup expanding to all todo leaves ```
defineTags
} from "@tanstack-tools/query-tags"
export const
const appTags: ResolvedTagTree<{
    todos: {
        list: () => string[];
        summary: () => string[];
        byId: (id: string) => string[];
    };
    notes: {
        list: () => string[];
    };
}>
appTags
=
defineTags<{
    todos: {
        list: () => string[];
        summary: () => string[];
        byId: (id: string) => string[];
    };
    notes: {
        list: () => string[];
    };
}>(factories: {
    todos: {
        list: () => string[];
        summary: () => string[];
        byId: (id: string) => string[];
    };
    notes: {
        list: () => string[];
    };
}): ResolvedTagTree<{
    todos: {
        list: () => string[];
        summary: () => string[];
        byId: (id: string) => string[];
    };
    notes: {
        list: () => string[];
    };
}>
Builds a typed, callable tag tree from a plain factory object. Each leaf becomes a tag factory stamped with its `TAG_PATH`; each branch becomes a callable `TagGroup` that expands to all descendant leaves when invoked without arguments.
@paramfactories - Nested object of tag factory functions.@returnsA fully-typed tag tree with autocomplete and compile-time safety.@example```ts const appTags = defineTags({ todos: { list: () => ["todos"], byId: (id: string) => ["todo", id] } }); appTags.todos.list(); // leaf tag value appTags.todos(); // TagGroup expanding to all todo leaves ```
defineTags
({
todos: {
    list: () => string[];
    summary: () => string[];
    byId: (id: string) => string[];
}
todos
: {
// group tag — invalidates all children list: () => string[]list: () => ["todos"], summary: () => string[]summary: () => ["todos", "summary"], byId: (id: string) => string[]byId: (id: stringid: string) => ["todo", id: stringid], },
notes: {
    list: () => string[];
}
notes
: {
list: () => string[]list: () => ["notes"], }, }) // Usage — fully typed:
const appTags: ResolvedTagTree<{
    todos: {
        list: () => string[];
        summary: () => string[];
        byId: (id: string) => string[];
    };
    notes: {
        list: () => string[];
    };
}>
appTags
.todos: () => TagGrouptodos() // invalidates list + summary + byId(*)
const appTags: ResolvedTagTree<{
    todos: {
        list: () => string[];
        summary: () => string[];
        byId: (id: string) => string[];
    };
    notes: {
        list: () => string[];
    };
}>
appTags
.
todos: TagNode<{
    list: () => string[];
    summary: () => string[];
    byId: (id: string) => string[];
}>
todos
.list: () => string[]list() // ["todos"] only
const appTags: ResolvedTagTree<{
    todos: {
        list: () => string[];
        summary: () => string[];
        byId: (id: string) => string[];
    };
    notes: {
        list: () => string[];
    };
}>
appTags
.
todos: TagNode<{
    list: () => string[];
    summary: () => string[];
    byId: (id: string) => string[];
}>
todos
.byId: (id: string) => string[]byId("1") // ["todo", "1"] only

Want to see which queries match which tags? Try the interactive Tag Playground →

Next step

Ready to set it up?

The guide walks through every setup step with working code examples.