{"record":{"id":"a8fcde7f605c5eef","repo":"withastro/astro","slug":"astro-the-key-directive-cannot-be-applied-d","errorCode":null,"errorMessage":"[astro] The \"${key}\" directive cannot be applied dynamically at runtime. It will not be rendered as an attribute.\n\nMake sure to use the static attribute syntax (`${key}={value}`) instead of the dynamic spread syntax (`{...{ \"${key}\": value }}`).","messagePattern":"\\[astro\\] The \"(.+?)\" directive cannot be applied dynamically at runtime\\. It will not be rendered as an attribute\\.\n\nMake sure to use the static attribute syntax \\(`(.+?)=(.+?)`\\) instead of the dynamic spread syntax \\(`(.+?)\\}`\\)\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"packages/astro/src/runtime/server/render/util.ts","lineNumber":102,"sourceCode":"}\n\n// A helper used to turn expressions into attribute key/value\n// In the compiler, addAttribute is only printed to process attributes of elements\n// that may contain dynamic values. We don't need to pass tagName to addAttribute\n// on the compiler side because it is used only for custom elements\nexport function addAttribute(value: any, key: string, shouldEscape = true, tagName = '') {\n\tif (value == null) {\n\t\treturn '';\n\t}\n\n\t// Reject attribute names with characters that could break out of the attribute context.\n\tif (INVALID_ATTR_NAME_CHAR.test(key)) {\n\t\treturn '';\n\t}\n\n\t// compiler directives cannot be applied dynamically, log a warning and ignore.\n\tif (STATIC_DIRECTIVES.has(key)) {\n\t\tconsole.warn(`[astro] The \"${key}\" directive cannot be applied dynamically at runtime. It will not be rendered as an attribute.\n\nMake sure to use the static attribute syntax (\\`${key}={value}\\`) instead of the dynamic spread syntax (\\`{...{ \"${key}\": value }}\\`).`);\n\t\treturn '';\n\t}\n\n\t// support \"class\" from an expression passed into an element (#782)\n\tif (key === 'class:list') {\n\t\tconst listValue = toAttributeString(clsx(value), shouldEscape);\n\t\tif (listValue === '') {\n\t\t\treturn '';\n\t\t}\n\t\treturn markHTMLString(` ${key.slice(0, -5)}=\"${listValue}\"`);\n\t}\n\n\t// support object styles for better JSX compat\n\tif (key === 'style' && !(value instanceof HTMLString)) {\n\t\tif (Array.isArray(value) && value.length === 2) {\n\t\t\treturn markHTMLString(","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/withastro/astro/blob/52e6c34790cc8ac4e69e6135ace06049867e5c4a/packages/astro/src/runtime/server/render/util.ts#L84-L120","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\n<div {...{ 'class:list': ['a', { active: isActive }] }} />\n\n// after\n<div class:list={['a', { active: isActive }]} />","handlingStrategy":"validation","validationCode":"// Split spreadable props from compiler directives before spreading\nconst DIRECTIVE_KEYS = new Set(['class:list', 'set:html', 'set:text', 'is:raw']);\nexport function splitDirectives(props: Record<string, unknown>) {\n  const spreadable: Record<string, unknown> = {};\n  const directives: Record<string, unknown> = {};\n  for (const [k, v] of Object.entries(props)) {\n    (DIRECTIVE_KEYS.has(k) ? directives : spreadable)[k] = v;\n  }\n  return { spreadable, directives }; // apply directives as static attributes\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["astro","directives","spread-props","class-list","attribute-rendering"],"backgroundTag":"dynamic-directive-not-supported","analyzedSha":"52e6c34790cc8ac4e69e6135ace06049867e5c4a","analyzedAt":"2026-08-18T18:48:03.901Z","contentChangedAt":"2026-08-18T18:48:03.901Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}