withastro/astro · error · Error
Invalid transition name {${transitionName}}
Error message
Invalid transition name {${transitionName}} What it means
`renderTransition` handles `transition:name` rendering for View Transitions. The transition name must be a string (a CSS identifier is derived from it via `cssesc`). If `transitionName` (null-coalesced to `''`) is not of type `string` — e.g. a number, boolean, object, or array — Astro cannot build a valid CSS custom-identifier and throws.
Source
Thrown at packages/astro/src/runtime/server/transition.ts:95
codepoint < 0x80
? codepoint === 95
? '__'
: (reEncodeValidChars[codepoint] ?? '_' + codepoint.toString(16).padStart(2, '0'))
: String.fromCodePoint(codepoint);
}
}
// Digits and minus sign at the beginning of the string are special, so we simply prepend an underscore
return reEncodeInValidStart[result.codePointAt(0) ?? 0] ? '_' + result : result;
}
export function renderTransition(
result: SSRResult,
hash: string,
animationName: TransitionAnimationValue | undefined,
transitionName: string,
) {
if (typeof (transitionName ?? '') !== 'string') {
throw new Error(`Invalid transition name {${transitionName}}`);
}
// Default to `fade` (similar to `initial`, but snappier)
if (!animationName) animationName = 'fade';
const scope = createTransitionScope(result, hash);
const name = transitionName ? cssesc(reEncode(transitionName), { isIdentifier: true }) : scope;
const sheet = new ViewTransitionStyleSheet(scope, name);
const animations = getAnimations(animationName);
if (animations) {
addPairs(animations, sheet);
} else if (animationName === 'none') {
sheet.addFallback('old', 'animation: none; mix-blend-mode: normal;');
sheet.addModern('old', 'animation: none; opacity: 0; mix-blend-mode: normal;');
sheet.addAnimationRaw('new', 'animation: none; mix-blend-mode: normal;');
sheet.addModern('group', 'animation: none');
}
const css = escapeStyleText(sheet.toString());View on GitHub (pinned to d081033d5f)
Solutions
- Ensure the `transition:name` value is always a string: `transition:name={String(id)}`.
- Coerce numeric/computed values before binding them.
- Validate dynamic values when iterating lists that produce transition names.
Example fix
// before
<div transition:name={item.id} /> // item.id is a number -> throws
// after
<div transition:name={String(item.id)} /> Defensive patterns
Strategy: type-guard
Validate before calling
function assertTransitionName(name: unknown): asserts name is string {
if (typeof (name ?? '') !== 'string') {
throw new Error(`Invalid transition name {${String(name)}}`);
}
} Type guard
const isTransitionName = (v: unknown): v is string =>
typeof (v ?? '') === 'string';
// In template:
transition:name={isTransitionName(name) ? name : String(name ?? '')} Prevention
- Always coerce transition names to strings, especially numeric IDs.
- Type `transition:name` props as `string`.
- Validate dynamic transition names when rendering lists.
When it happens
Trigger: Passing `transition:name={123}` (number), a boolean, an object/array, or `undefined` that survives the `?? ''` coercion to a non-string type; an expression returning a non-string used as the transition name.
Common situations: Dynamic transition names computed from numeric IDs without string conversion; binding `transition:name` to a data field that is occasionally a number; spread-props carrying a non-string value.
Related errors
AI-assisted analysis of withastro/astro@d081033d5f (2026-08-12).
Data as JSON: /api/errors/1b414851aff5157d.
Report an issue: GitHub.