skip to content

In Vue 3, how can a `<script setup>` component in TreeItem.vue render itself for nested folders, and what can override that self-reference?

level: middleimportance: should knowfreq 40%

answer

  1. the file name is a name
  2. a tag matching its own file
  3. a base case stops it
  4. imports beat self-reference
  5. alias the conflicting import

basics

~20 s

A Vue SFC infers its name from its filename, so TreeItem.vue can render <TreeItem> in its own template without importing itself. An import with the same name takes priority; alias that import, and guard recursion with a condition on the children.

solid answer

~40 s

An SFC infers its name from its filename, so `TreeItem.vue` can write `<TreeItem>` inside its own template and Vue resolves it to the component itself; there is no need to import the file into itself. Recursion must end somewhere, so render children only when they exist, e.g. `v-for` over `node.children` inside a `v-if`. The self-reference is the **lowest-priority** match: in `<script setup>`, an imported binding named `TreeItem` wins at compile time, and at runtime an explicit `name`, a local registration or a global registration under that name is found before the implicit self-reference. If an import collides with the filename, alias it (`import { TreeItem as TreeItemChild } from ...`). You can also set an explicit name with `defineOptions({ name: 'FolderNode' })` (3.3+), which the component can then use to refer to itself.

go deeper

for a junior

Know that TreeItem.vue can use <TreeItem> in its own template and that the recursion needs a condition to stop.

for a middle

Explain filename inference and the lookup order that makes the self-reference a last-resort fallback.

for a senior

Debug a tree that suddenly renders the wrong component after a new import or global registration, and fix it with an alias or explicit name.

for a principal

Set naming conventions in a component library so recursive and generic names such as TreeItem do not collide across packages.

## Why a recursive component needs a name A **recursive component** renders itself inside its own template. The classic case is a folder tree: `TreeItem` shows one folder and, for each child folder, another `TreeItem`. For the inner `<TreeItem>` tag to resolve, the component must be able to find itself by name, but in `<script setup>` there is nothing to import, because a file cannot sensibly import itself. Vue solves this with **filename inference**. When an SFC is compiled, the compiler records the file's base name. A tag in that file's template whose PascalCase form equals the filename (`<TreeItem>` or `<tree-item>` in `TreeItem.vue`) is compiled as a possible self-reference. ## A folder tree ```vue <script setup lang="ts"> import { ref } from 'vue' interface Folder { name: string children?: Folder[] } const props = defineProps<{ folder: Folder }>() const open = ref(false) </script> <template> <li> <span @click="open = !open">{{ props.folder.name }}</span> <ul v-if="open && props.folder.children?.length"> <TreeItem v-for="child in props.folder.children" :key="child.name" :folder="child" /> </ul> </li> </template> ``` Two things keep this correct: - the **base case**: a leaf folder has no children, so the `v-if` renders nothing and recursion stops. A template that renders `<TreeItem>` unconditionally recurses until the call stack overflows; - the **key** on each child, so Vue can match children across updates when folders are added or reordered. ## The resolution order The filename self-reference is a fallback, not an override. For a tag such as `<TreeItem>`: 1. **Compile time, `<script setup>` bindings.** If the file imports or declares something named `TreeItem`, the template uses that binding directly. The self-reference is never consulted. 2. **Runtime, explicit self name.** If the component has an explicit `name` (for example via `defineOptions`), a tag matching it resolves to the component itself. 3. **Runtime, registries.** Local registrations, then components registered globally with `app.component()`. 4. **Runtime, implicit self-reference.** Only if nothing above matched does the inferred filename name resolve to the component itself. The consequence is easy to miss: a globally registered component that happens to be called `TreeItem` would be rendered *instead of* the file's own recursion. ## Fixing a name collision Suppose a `TreeItem.vue` for the sidebar also wants to render a *different* `TreeItem` from a shared library. The import wins, so the sidebar can no longer recurse into itself. The Vue docs recommend **aliasing the import**: ```ts import { TreeItem as LibraryTreeItem } from './library/tree' ``` Now `<LibraryTreeItem>` means the library component and `<TreeItem>` falls through to the self-reference again. ## Explicit names | Source of the name | How it is set | Used for | |---|---|---| | Filename inference | Automatic for SFCs | Self-reference, devtools, warning traces | | `defineOptions({ name })` | Explicitly in `<script setup>` (3.3+) | Overrides the inferred name everywhere | | `name` option | In a non-`<script setup>` component | Same, Options API style | An explicit name is useful when a component is not an SFC (no filename to infer from) or when its display name should differ from its file.

  • A Vue 3 `TreeItem.vue` renders `<TreeItem>` for its children, but a global `app.component('TreeItem', OtherTree)` exists. Which component renders for the children?
    `OtherTree`. At runtime the lookup checks the component's explicit name, then local and global registrations, and falls back to the filename self-reference only when nothing matched. The global registration matches first, so the file's own recursion is bypassed. Rename one of them or give the file an explicit name.
  • What happens if a Vue 3 recursive component renders itself without any condition?
    Each instance renders another instance during its own render, with no data change able to stop it, so the render recursion continues until the JavaScript call stack overflows. Always tie the recursive tag to data that eventually runs out, such as `v-for` over a folder's `children` inside a `v-if`.

saying these in an interview costs you the question

  • A script setup component must import its own file to render itself recursively.
  • The filename self-reference overrides any import with the same name.
  • Vue detects unbounded recursion and stops it after a fixed depth.
  • Recursive components only work if you declare a name option.