withastro/astro · warning
[astro] The "${key}" directive cannot be applied dynamically
Error message
[astro] The "${key}" directive cannot be applied dynamically at runtime. It will not be rendered as an attribute.
Make sure to use the static attribute syntax (`${key}={value}`) instead of the dynamic spread syntax (`{...{ "${key}": value }}`). What it means
addAttribute renders runtime attribute strings, but compiler directives (STATIC_DIRECTIVES such as class:list, set:html, set:text, is:raw) are compiled away at build time. When one arrives through a dynamic spread, Astro cannot apply it at runtime: it logs this warning and returns '' so nothing broken reaches the HTML. The directive must be written as a static attribute for the compiler to see it.
Source
Thrown at packages/astro/src/runtime/server/render/util.ts:102
}
// A helper used to turn expressions into attribute key/value
// In the compiler, addAttribute is only printed to process attributes of elements
// that may contain dynamic values. We don't need to pass tagName to addAttribute
// on the compiler side because it is used only for custom elements
export function addAttribute(value: any, key: string, shouldEscape = true, tagName = '') {
if (value == null) {
return '';
}
// Reject attribute names with characters that could break out of the attribute context.
if (INVALID_ATTR_NAME_CHAR.test(key)) {
return '';
}
// compiler directives cannot be applied dynamically, log a warning and ignore.
if (STATIC_DIRECTIVES.has(key)) {
console.warn(`[astro] The "${key}" directive cannot be applied dynamically at runtime. It will not be rendered as an attribute.
Make sure to use the static attribute syntax (\`${key}={value}\`) instead of the dynamic spread syntax (\`{...{ "${key}": value }}\`).`);
return '';
}
// support "class" from an expression passed into an element (#782)
if (key === 'class:list') {
const listValue = toAttributeString(clsx(value), shouldEscape);
if (listValue === '') {
return '';
}
return markHTMLString(` ${key.slice(0, -5)}="${listValue}"`);
}
// support object styles for better JSX compat
if (key === 'style' && !(value instanceof HTMLString)) {
if (Array.isArray(value) && value.length === 2) {
return markHTMLString(View on GitHub (pinned to 52e6c34790)
Solutions
- Apply the directive statically at the call site: class:list={...} or set:html={...} instead of spreading
- Filter directive keys out of the object before spreading and handle them as explicit static attributes
- For dynamic classes use a plain class={...} or a statically written class:list with computed values
Example fix
// before
<div {...{ 'class:list': ['a', { active: isActive }] }} />
// after
<div class:list={['a', { active: isActive }]} /> Defensive patterns
Strategy: validation
Validate before calling
// Split spreadable props from compiler directives before spreading
const DIRECTIVE_KEYS = new Set(['class:list', 'set:html', 'set:text', 'is:raw']);
export function splitDirectives(props: Record<string, unknown>) {
const spreadable: Record<string, unknown> = {};
const directives: Record<string, unknown> = {};
for (const [k, v] of Object.entries(props)) {
(DIRECTIVE_KEYS.has(k) ? directives : spreadable)[k] = v;
}
return { spreadable, directives }; // apply directives as static attributes
} Prevention
- Never blind-spread unknown props onto elements in .astro templates
- Keep class:list / set:html as literal attributes at the call site
- Log filtered directive keys in dev to catch accidental spreading early
When it happens
Trigger: Spreading an object containing a directive key: <div {...{ 'class:list': [...] }} />, or a wrapper component forwarding arbitrary props ({...rest}) where the props object happens to include set:html, set:text, class:list, or is:raw.
Common situations: Generic wrapper components that spread all props onto an element; porting React forwarding patterns to .astro; prop objects assembled dynamically from data or CMS content.
Related errors
- ⚠️ Astro expected an SVG for "${transform.src}" but the sou
- ⚠️ Astro could not optimize image "${transform.src}". Sharp
- [content] Could not read the chunked data store at ${fileURL
- NoImageMetadata
- FailedToFetchRemoteImageDimensions
AI-assisted analysis of withastro/astro@52e6c34790 (2026-08-18).
Data as JSON: /api/errors/a8fcde7f605c5eef.
Report an issue: GitHub.