Menu
next-query

Tags

Plain cache tags, the tags() hierarchy, escaping and limits.

A tag is a plain string that Next attaches to a cached entry, and revalidate() expires by it. You can tag a native fetch with any string, and tags() builds a hierarchy of tags from a key when you want one. This page has the exact rules.

Plain tags

Tag a fetch with next.tags. There is no prefix or namespace: the tag you write is the tag Next stores.

ts
fetch(`${API}/products`, { next: { tags: ['products'], revalidate: 60 } })
revalidate('products') // expires the tag 'products'

The tags() hierarchy

tags(key) turns a key into one tag per segment. The segments are joined with /:

CallReturns
tags('products')['products']
tags(['products', 1])['products', 'products/1']
tags(['products', 1, 'reviews'])['products', 'products/1', 'products/1/reviews']

For hierarchies pass an array: tags('products/1') is ONE segment (products%2F1); use tags(['products', 1]).

Use it on the fetch:

ts
import { tags } from '@angelitolm/next-query'
 
fetch(`${API}/products/${id}`, { next: { tags: tags(['products', id]) } })

Every product carries the tag products, so revalidate('products') reaches all of them, and revalidate(['products', 1]) reaches only product 1. That is how one tag covers a whole branch.

revalidate(key) with an array expires the key's deepest tag, the same as revalidate(tags(key).at(-1)). So revalidate('products/1') equals revalidate(['products', 1]).

query() tags its entry with tags(key), so a query and a fetch share the same scheme. See query.

Key rules

tags() and query() validate the key, and revalidate() validates it when you pass an array:

  • The key is a non-empty array. In tags(), a string is shorthand for a one-segment key.
  • Each segment is a non-empty string or a finite number. NaN, Infinity, '', null, objects and booleans throw a TypeError.
Valid and invalid keys
ts
tags(['products'])               // ok
tags(['products', 1, 'reviews']) // ok
tags([])                         // TypeError: key must be a non-empty array
tags(['products', ''])           // TypeError: key segments must be non-empty strings or finite numbers
tags(['products', NaN])          // TypeError

['products', 1] and ['products', '1'] give the same tags. As query() keys they are different cache entries, because a query is keyed by the JSON of its key.

Escaping

/ separates segments inside a tag, so it is escaped when it appears inside one: / becomes %2F and % becomes %25. This keeps keys apart that would otherwise collide:

KeyTags
['a/b']['a%2Fb']
['a', 'b']['a', 'a/b']

So ['a/b'] and ['a', 'b'] are unrelated, and revalidate(['a']) does not reach ['a/b'].

Limits

Next limits the tags of a cache entry. tags(), query() and revalidate() check both limits up front and throw a TypeError:

  • 256 characters per tag. A long segment counts, and so does escaping. A string passed to revalidate() must be 1 to 256 characters.
  • 128 tags per entry. A key makes one tag per segment, so a key can have at most 128 segments.

Keep keys short and structured

A key like ['posts', slug] is a name, not a payload. Put a value in the key only when it changes what the data is.

Revalidate all

Revalidate all in the panel revalidates every tag in the list (the filter does not narrow it), in batches of 128. It does not touch a tag that no listed entry carries.