diff --git a/src/guide/extras/web-components.md b/src/guide/extras/web-components.md
index 6bf9a5de45..75b72bbc17 100644
--- a/src/guide/extras/web-components.md
+++ b/src/guide/extras/web-components.md
@@ -220,6 +220,8 @@ If the custom elements will be used in an application that is also using Vue, yo
It is recommended to export the individual element constructors to give your users the flexibility to import them on-demand and register them with desired tag names. You can also export a convenience function to automatically register all elements. Here's an example entry point of a Vue custom element library:
```js
+// elements.js
+
import { defineCustomElement } from 'vue'
import Foo from './MyFoo.ce.vue'
import Bar from './MyBar.ce.vue'
@@ -236,31 +238,277 @@ export function register() {
}
```
-If you have many components, you can also leverage build tool features such as Vite's [glob import](https://vitejs.dev/guide/features.html#glob-import) or webpack's [`require.context`](https://webpack.js.org/guides/dependency-management/#requirecontext) to load all components from a directory.
+A consumer can use the elements in a Vue file,
+
+```vue
+
+
+
+
+
+
+
+```
+
+or in any other framework such as one with JSX, and with custom names:
-### Web Components and TypeScript {#web-components-and-typescript}
+```jsx
+import { MyFoo, MyBar } from 'path/to/elements.js'
+
+customElements.define('some-foo', MyFoo)
+customElements.define('some-bar', MyBar)
+
+export function MyComponent() {
+ return <>
+
+
+
+ >
+}
+```
-If you are developing an application or a library, you may want to [type check](/guide/scaling-up/tooling.html#typescript) your Vue components, including those that are defined as custom elements.
+### Vue-based Web Components and TypeScript {#web-components-and-typescript}
-Custom elements are registered globally using native APIs, so by default they won't have type inference when used in Vue templates. To provide type support for Vue components registered as custom elements, we can register global component typings using the the [`GlobalComponents` interface](https://github.com/vuejs/language-tools/blob/master/packages/vscode-vue/README.md#usage) in Vue templates and/or in [JSX](https://www.typescriptlang.org/docs/handbook/jsx.html#intrinsic-elements):
+When writing Vue SFC templates, you may want to [type check](/guide/scaling-up/tooling.html#typescript) your Vue components, including those that are defined as custom elements.
+
+Custom elements are registered globally in browsers using their built-in APIs, and by default they won't have type inference when used in Vue templates. To provide type support for Vue components registered as custom elements, we can register global component typings by augmenting the [`GlobalComponents` interface](https://github.com/vuejs/language-tools/blob/master/packages/vscode-vue/README.md#usage) for type checking in Vue templates (JSX users can augment the [JSX.IntrinsicElements](https://www.typescriptlang.org/docs/handbook/jsx.html#intrinsic-elements) type instead, which is not shown here).
+
+Here is how to define the type for a custom element made with Vue:
```typescript
import { defineCustomElement } from 'vue'
-// vue SFC
-import CounterSFC from './src/components/counter.ce.vue'
+// Import the Vue component.
+import SomeComponent from './src/components/SomeComponent.ce.vue'
+
+// Turn the Vue component into a Custom Element class.
+export const SomeElement = defineCustomElement(SomeComponent)
+
+// Remember to register the element class with the browser.
+customElements.define('some-element', SomeElement)
+
+// Add the new element type to Vue's GlobalComponents type.
+declare module 'vue' {
+ interface GlobalComponents {
+ // Be sure to pass in the Vue component type here (SomeComponent, *not* SomeElement).
+ // Custom Elements require a hyphen in their name, so use the hyphenated element name here.
+ 'some-element': typeof SomeComponent
+ }
+}
+```
+
+## Non-Vue Web Components and TypeScript
+
+Here is the recommended way to enable type checking in SFC templates of Custom
+Elements that are not built with Vue.
+
+> [!Note]
+> This approach is one possible way to do it, but it may vary depending on the
+> framework being used to create the custom elements.
+
+Suppose we have a custom element with some JS properties and events defined, and
+it is shipped in a library called `some-lib`:
+
+```ts
+// file: some-lib/src/SomeElement.ts
+
+// Define a class with typed JS properties.
+export class SomeElement extends HTMLElement {
+ foo: number = 123
+ bar: string = 'blah'
+
+ lorem: boolean = false
+
+ // This method should not be exposed to template types.
+ someMethod() {
+ /* ... */
+ }
+
+ // ... implementation details omitted ...
+ // ... assume the element dispatches events named "apple-fell" ...
+}
+
+customElements.define('some-element', SomeElement)
+
+// This is a list of properties of SomeElement that will be selected for type
+// checking in framework templates (f.e. Vue SFC templates). Any other
+// properties will not be exposed.
+export type SomeElementAttributes = 'foo' | 'bar'
+
+// Define the event types that SomeElement dispatches.
+export type SomeElementEvents = {
+ 'apple-fell': AppleFellEvent
+}
+
+export class AppleFellEvent extends Event {
+ /* ... details omitted ... */
+}
+```
+
+The implementation details have been omitted, but the important part is that we
+have type definitions for two things: prop types and event types.
+
+Let's create a type helper for easily registering custom element type
+definitions in Vue:
+
+```ts
+// file: some-lib/src/DefineCustomElement.ts
+
+// We can re-use this type helper per each element we need to define.
+type DefineCustomElement<
+ ElementType extends HTMLElement,
+ Events extends EventMap = {},
+ SelectedAttributes extends keyof ElementType = keyof ElementType
+> = new () => ElementType & {
+ // Use $props to define the properties exposed to template type checking. Vue
+ // specifically reads prop definitions from the `$props` type. Note that we
+ // combine the element's props with the global HTML props and Vue's special
+ // props.
+ /** @deprecated Do not use the $props property on a Custom Element ref, this is for template prop types only. */
+ $props: HTMLAttributes &
+ Partial> &
+ PublicProps
+
+ // Use $emit to specifically define event types. Vue specifically reads event
+ // types from the `$emit` type. Note that `$emit` expects a particular format
+ // that we map `Events` to.
+ /** @deprecated Do not use the $emit property on a Custom Element ref, this is for template prop types only. */
+ $emit: VueEmit
+}
+
+type EventMap = {
+ [event: string]: Event
+}
+
+// This maps an EventMap to the format that Vue's $emit type expects.
+type VueEmit = EmitFn<{
+ [K in keyof T]: (event: T[K]) => void
+}>
+```
+
+> [!Note]
+> We marked `$props` and `$emit` as deprecated so that when we get a `ref` to a
+> custom element we will not be tempted to use these properties, as these
+> properties are for type checking purposes only when it comes to custom elements.
+> These properties do not actually exist on the custom element instances.
+
+Using the type helper we can now select the JS properties that should be exposed
+for type checking in Vue templates:
+
+```ts
+// file: some-lib/src/SomeElement.vue.ts
+
+import {
+ SomeElement,
+ SomeElementAttributes,
+ SomeElementEvents
+} from './SomeElement.js'
+import type { Component } from 'vue'
+import type { DefineCustomElement } from './DefineCustomElement'
+
+// Add the new element type to Vue's GlobalComponents type.
+declare module 'vue' {
+ interface GlobalComponents {
+ 'some-element': DefineCustomElement<
+ SomeElement,
+ SomeElementAttributes,
+ SomeElementEvents
+ >
+ }
+}
+```
-// turn component into web components
-export const Counter = defineCustomElement(CounterSFC)
+Suppose that `some-lib` builds its source TypeScript files into a `dist/` folder. A user of
+`some-lib` can then import `SomeElement` and use it in a Vue SFC like so:
-// register global typings
+```vue
+
+
+
+
+ {
+ // The type of `event` is inferred here to be `AppleFellEvent`
+ }
+ "
+ >
+
+```
+
+If an element does not have type definitions, the types of the properties and events can be
+defined in a more manual fashion:
+
+```vue
+
+
+
+
+
```
+Custom Element authors should not automatically export framework-specific custom
+element type definitions from their libraries, for example they should not
+export them from an `index.ts` file that also exports the rest of the library,
+otherwise users will have unexpected module augmentation errors. Users should
+import the framework-specific type definition file that they need.
+
## Web Components vs. Vue Components {#web-components-vs-vue-components}
Some developers believe that framework-proprietary component models should be avoided, and that exclusively using Custom Elements makes an application "future-proof". Here we will try to explain why we believe that this is an overly simplistic take on the problem.