Dynamic and Built-In Components
By Syncfusion®, Inc. · Last updated
Switching components with <component :is>, async components with defineAsyncComponent() and their loading, error, delay and timeout options, code splitting, <KeepAlive> with include, exclude, max and activation hooks, <Teleport> for modals and overlays with focus handling, and the experimental <Suspense>.
Components That Change
- Sometimes which component to show is decided while the app runs.
- Tabs, wizard steps and widgets on a dashboard all work like this.
Vue has one tag for it,
<component :is>, and four built-in helpers:defineAsyncComponentloads a component only when needed<KeepAlive>remembers a component's state when it is hidden<Teleport>shows content somewhere else on the page<Suspense>waits for async components to be ready
Simple lesson: :is picks a component at run time. Built-in components control how it loads, lives and where it appears.
A Real-World Example
- Meera's dish page has three tabs: Details, Reviews and Nutrition.
- Each tab is its own component.
- One
<component :is>shows the chosen one.
- What happened: each button changed
current.<component :is>showed that component. dishwas passed as a prop, like on any component.
What You Will Learn
- How
<component :is>works. - Why to store components in
shallowRef. - How to load a component later with
defineAsyncComponent. - Loading, error, delay and timeout options.
- How code splitting makes the first load faster.
- How
<KeepAlive>keeps state. - The
onActivatedandonDeactivatedhooks. - How
<Teleport>moves a modal to the end of the page. - What
<Suspense>does, and why it is still experimental.
component :is
:istakes a component object or the name of a registered component.- It can also take an HTML tag name, such as
"button"or"a". - Props, events and slots work as usual.
- Switching removes the old component and its state.
<KeepAlive>changes that, below.
Store component objects in shallowRef or a plain object, not ref. A deep ref would make the whole component definition reactive, which is wasted work. Vue warns about it.
Simple lesson: <component :is="X" /> renders whatever component X holds.
Async Components
defineAsyncComponent(() => import("./Big.vue"))makes a component that loads its code the first time it is shown.- Until then, its code is not part of the first download.
- Code splitting = the build puts that file in its own small chunk.
Options make it friendly:
loadingComponent: shown while loadingdelay: wait this long before showing it (default 200 ms), to avoid a flasherrorComponent: shown if loading failstimeout: give up after this long
- What happened: "Loading the chart…" showed, then the chart.
- The broken widget showed its error component instead of a blank space.
In a real build, import("./MenuChart.vue") downloads a separate JavaScript file the first time. In this lab, every file is already in the preview, so the box adds a fake delay to show the loading state. Routes can be split the same way, as in Navigation Guards and Advanced Routing.
Simple lesson: defineAsyncComponent loads code only when needed. Give it loading and error parts.
KeepAlive
- Normally, switching away from a component destroys it and its state.
- Wrap the dynamic component in
<KeepAlive>to keep it in memory instead. includeandexcludechoose which components to keep, by name. A<script setup>component is named after its file (Reviews.vue→Reviews), or set one withdefineOptions({ name }).maxlimits how many are kept. The least recently used is dropped first.- Kept components get
onActivatedandonDeactivatedhooks instead of mount and unmount.
- What happened: your note was still there after switching back.
- The Console shows
deactivatedandactivatedinstead of unmount and mount.
include and exclude match the component's name. A <script setup> component is named after its file, such as OrderNote. Use defineOptions({ name }) if you need a different name.
Simple lesson: <KeepAlive> hides components instead of destroying them.
Teleport
- A modal is written inside a component, but it should appear on top of the whole page.
- Deep inside other elements, CSS like
overflow: hiddenorz-indexcan trap it. <Teleport to="body">renders its content at the end of<body>, but keeps it part of the component.- State, props and events still work normally.
:disabled="true"keeps it in place, for example on small screens.
Animation: a modal written inside OrderCard is moved by Teleport to the end of the body element, outside the card, while its logic stays in the component
OrderCard (overflow: hidden)
end of
Confirm order? modal
Written in OrderCard. Shown at the end of body. Logic stays in OrderCard.
Teleport moves where the HTML appears, not where the code lives.
- What happened: the modal escaped the card's
overflow: hiddenbox. - Focus moved into the modal, and back to the button after closing. Esc also closes it.
A good modal: has role="dialog" and aria-modal="true", moves focus inside when it opens, closes on Esc, and returns focus to the button that opened it.
Simple lesson: <Teleport to="body"> puts modals and overlays at the end of the page.
Suspense (Experimental)
- An async component here means a
<script setup>that usesawaitat the top level. <Suspense>waits until every async child below it is ready.- Until then, it shows its
#fallbackslot. - Events:
pending,resolveandfallback. - It is still experimental: its API may change. Many apps show their own loading state instead.
Simple lesson: <Suspense> shows a fallback until async children are ready. Use it with care.
TL;DR
<component :is>= renders the component (or tag) held in a variable.shallowRef= the right way to store a component object.defineAsyncComponent= loads a component's code on first use.- Async options =
loadingComponent,delay,errorComponent,timeout. - Code splitting = lazy parts become separate, smaller files.
<KeepAlive>= keeps hidden components and their state.include,exclude,max.onActivated/onDeactivated= hooks for kept components.<Teleport to="body">= renders content elsewhere, keeps the logic here.<Suspense>= experimental. Shows#fallbackuntil async children are ready.
Common mistakes
| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
Component in a deep ref | Any ref works | Use shallowRef | A warning and wasted work |
| No loading or error component | Lazy parts load instantly | Set loadingComponent and errorComponent | Blank space, or nothing on failure |
| Expecting state to survive a switch | Vue keeps it | Wrap in <KeepAlive> | Typed text is lost |
include with the wrong name | It matches the tag | It matches the component name | Nothing is kept |
Modal inside overflow: hidden | z-index fixes it | <Teleport to="body"> | The modal is cut off or hidden |
| Modal without focus handling | Clicking is enough | Focus in, Esc to close, focus back | Keyboard users get lost |
| Suspense everywhere | It is a stable core feature | Use it sparingly, it is experimental | Code may break on upgrades |
Check Yourself
Exercises
What to Remember
<component :is>switches components. Store them inshallowRef.defineAsyncComponentloads code on demand. Add loading and error parts.<KeepAlive>keeps state. Kept components getonActivatedandonDeactivated.<Teleport to="body">frees modals from parent CSS. Handle focus and Esc.<Suspense>is experimental. Prefer your own loading states for now.
Next: Transitions and Animations