[data-reveal]{opacity:1!important;transform:none!important}
Components
Frontend

Components

How registered components get their global name, how a module prefix avoids collisions, how to replace another module's component, and the SVG caveat.

Every component a module registers joins the application's global components and resolves by name anywhere: in templates and in the page trees the backend sends.

To resolve a registered component in code, import resolveDmsComponent explicitly from #dms/frontend-module:

import { resolveDmsComponent } from "#dms/frontend-module";

const Banner = resolveDmsComponent("AcmeBanner");

Component prefix

To keep the components of two modules from colliding, a module declares a componentPrefix. It goes in front of the name of every component the module registers:

dms.frontend.ts
const frontendModule: DmsFrontendModule = {
  componentPrefix: "Acme",
  setup(sdk) {
    sdk.registerComponent("Banner", Banner); // registered as AcmeBanner
  },
};

The full name, prefix included, is the one a template uses (<AcmeBanner>) and the one a backend sends (CustomComponent("AcmeBanner"), or acme-banner). Names are compared ignoring case, a leading lazy and non-alphanumeric characters. A leading Dms is not dropped: it is the DMS module's own prefix.

Collisions and replacements

When two modules register the same name, the module of higher priority keeps it, and a warning names both. A module that means to replace another module's component registers the full name without its own prefix, which is not reported:

dms.frontend.ts
sdk.registerComponent("AcmeBanner", MyBanner, { prefix: false });

Private code

A module's app/build/ directory is never auto-imported. Its components are registered so that backend pages can name them, but they are the module's internals, not a surface other modules should build on. See Auto-imports.

Page loading state

After a client navigation, the page renders under a <Suspense> until its chunk and any async setup resolve. useDmsPageLoading(), auto-imported, is true meanwhile, so a layout can show its own placeholder instead of an empty page body:

layouts/AppLayout.vue
<script setup lang="ts">
const pageLoading = useDmsPageLoading();
</script>

<template>
  <slot />
  <PageSkeleton v-if="pageLoading" />
</template>

It stays false on the server and during hydration, which render the resolved page, for a page that does not suspend, and once a pending page is left.

Components rendered inside an svg

Modules usually register their components lazily, with defineAsyncComponent, and the page renders under a <Suspense>. Vue mounts an async component that resolves under a <Suspense> with the <Suspense>'s element namespace rather than the namespace of the place it renders in. A component resolved by name whose root is an SVG element (<path>, <g>…) is then created as an HTML element inside the <svg>, and the browser draws nothing (vuejs/core#15639).

Import such components directly instead, for example the node and edge types passed to Vue Flow:

import { markRaw } from "vue";
import RelationEdge from "./RelationEdge.vue";

const edgeTypes = markRaw({ relation: RelationEdge });