oxc-project/oxc · error · OxcDiagnostic
'{prop_name}' is a reserved attribute and cannot be used as
Error message
'{prop_name}' is a reserved attribute and cannot be used as props. What it means
The vue/no-reserved-props rule reports props whose names collide with reserved template attributes. The source defines the sets per version: Vue 3 reserves `key` and `ref`; Vue 2 additionally reserves `is`, `slot`, `slot-scope`, `slotScope`, `class` and `style`. Declaring such a prop does not work — the template compiler consumes the attribute for its own purposes and never passes it as a prop. Which set applies is chosen by the rule's `vueVersion` option (deserialized via deserialize_vue_version); the rule checks both object-syntax components (is_vue_component_options_object) and `defineProps` type signatures, comparing kebab-cased names.
Source
Thrown at crates/oxc_linter/src/rules/vue/no_reserved_props.rs:32
use crate::{
AstNode,
context::LintContext,
frameworks::FrameworkOptions,
rule::{DefaultRuleConfig, Rule},
utils::{
for_each_define_props_type_signature, is_vue_component_options_object,
vue_casing::kebab_case,
},
};
/// Reserved attribute names that cannot be used as prop names, by Vue version.
const RESERVED_VUE3: &[&str] = &["key", "ref"];
const RESERVED_VUE2: &[&str] =
&["key", "ref", "is", "slot", "slot-scope", "slotScope", "class", "style"];
fn no_reserved_props_diagnostic(prop_name: &str, span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn(format!(
"'{prop_name}' is a reserved attribute and cannot be used as props."
))
.with_label(span)
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase", default, deny_unknown_fields)]
struct NoReservedPropsConfig {
/// Vue major version whose reserved attribute set is applied. Vue 2 reserves
/// more names (`is`, `slot`, `class`, `style`, ...) than Vue 3.
#[serde(deserialize_with = "deserialize_vue_version")]
vue_version: u8,
}
impl Default for NoReservedPropsConfig {
fn default() -> Self {
Self { vue_version: 3 }
}View on GitHub (pinned to e1e7af627c)
Solutions
- Rename the prop to a non-reserved name (e.g. `itemKey`, `variant`, `cssClass`).
- If you need the native attribute behavior (`class`/`style`/`is`), rely on Vue's fallthrough attributes instead of declaring a prop.
- Verify the rule's `vueVersion` option matches your project (Vue 2 reserves more names than Vue 3).
- Re-run oxlint to confirm.
Example fix
// before
export default {
props: ['key'] // reserved attribute
}
// after
export default {
props: ['itemKey']
} Defensive patterns
Strategy: validation
Validate before calling
// guard prop declarations at authoring time
const RESERVED = ['key', 'ref', 'is', 'slot', 'slot-scope', 'slotScope', 'class', 'style'];
function assertProps(props) {
for (const name of Object.keys(props)) {
if (RESERVED.includes(name)) throw new Error(`prop '${name}' is a reserved attribute`);
}
} Prevention
- Set the rule's `vueVersion` to match your project (Vue 2 reserves more attribute names).
- Use fallthrough attributes for class/style instead of declaring them as props.
- Rename key/ref/is props to itemKey/elementRef/componentIs style names.
When it happens
Trigger: Declaring `props: ['key']`, `props: ['ref']`, or in Vue 2 mode `is`/`slot`/`class`/`style`, including camelCase forms like `slotScope` and `defineProps<{ is: boolean }>()` signatures once kebab-cased.
Common situations: Components modeling HTML concepts (`class` for CSS, `is` for dynamic types) ported into Vue props; migrating Vue 2 components to Vue 3 while linting with the wrong `vueVersion`; generic table/list components that accept a `key` prop.
Related errors
- `VirtualFree` failed during cleanup: {err}
- Insufficient memory to create fixed-size allocator pool
- Fixed size allocators are only supported on 64-bit little-en
- This component has too many props ({cur}). Maximum allowed i
- Props default value factory functions no longer have access
AI-assisted analysis of oxc-project/oxc@e1e7af627c (2026-08-20).
Data as JSON: /api/errors/90c37a5cbc96a14b.
Report an issue: GitHub.