# Web Component _Path: en/frontend/micro-frontends/web-component_ ## Table of Contents - Web Component () ## Content # Web Component (`view.component`) A Wippy web component is a custom element (`view.component`) built with `WippyVueElement`. It runs inside the host page's DOM (not an iframe), communicates with the platform via `@wippy-fe/proxy`, and encapsulates its styles in a shadow root. > **Isolation is mandatory.** The bundle carries only source and `package.json`. The BE-side `view.component` registry entry declares the URL, tag name, props, and events per deployment. The registry entry wins over `package.json` for any overlapping field — `package.json` values are suggestions and host-less fallbacks. The same built artifact ships unchanged to any Wippy instance. For a comparison of when to choose a web component over a micro frontend app, see [Overview](./overview.md). ## Project structure ``` my-widget/ ├── package.json ├── vite.config.ts ├── tsconfig.json ├── tsconfig.node.json ├── .eslintrc.cjs ├── postcss.config.js # Only if using Tailwind ├── tailwind.config.ts # Only if using Tailwind └── src/ ├── index.ts # WippyVueElement subclass + define() ├── types.ts # ComponentProps interface ├── constants.ts # Events interface + typed composable wrappers ├── styles.css # Component styles ├── tailwind.css # @tailwind directives (if using Tailwind) └── app/ └── my-widget.vue # Vue root component ``` ## `package.json` — the `wippy` block ```json { "name": "@myorg/widget-my-widget", "version": "1.0.0", "specification": "wippy-component-1.0", "title": "My Widget", "description": "Description of what the widget does", "browser": "dist/index.js", "files": ["dist/", "src/", "package.json"], "dependencies": { "@wippy-fe/theme": "^0.0.34", "@wippy-fe/webcomponent-core": "^0.0.34", "@wippy-fe/webcomponent-vue": "^0.0.34" }, "devDependencies": { "@typescript-eslint/eslint-plugin": "^7.0.0", "@typescript-eslint/parser": "^7.0.0", "@vitejs/plugin-vue": "^5.0.0", "@wippy-fe/vite-plugin": "^0.0.34", "@wippy-fe/proxy": "^0.0.34", "typescript": "^5.0.0", "vite": "^6.0.0", "vue": "^3.5.0", "vue-tsc": "^2.0.0" }, "peerDependencies": { "@wippy-fe/proxy": "^0.0.34", "vue": "^3.5.0" }, "wippy": { "tagName": "myorg-my-widget", "type": "widget", "props": { "type": "object", "properties": { "title": { "type": "string", "default": "Hello", "description": "Widget title" }, "max-items": { "type": "number", "default": 10, "description": "Maximum number of items to display" } } }, "events": { "type": "object", "properties": { "item-selected": { "type": "object", "properties": { "id": { "type": "string" }, "label": { "type": "string" } }, "description": "Fired when the user selects an item" } } }, "scripts": { "build": "build", "debug": "build:debug", "test": "lint" } }, "scripts": { "build": "vite build", "build:debug": "vite build --mode development", "dev": "vite build --watch", "lint": "eslint src --ext .ts,.vue", "lint:fix": "eslint src --ext .ts,.vue --fix" } } ``` ### Field reference | Field | Required | Description | |---|---|---| | `specification` | Yes | Must be `"wippy-component-1.0"`. | | `browser` | Yes | Entry point for the browser ES module. Must be `"dist/index.js"`. | | `wippy.type` | Yes | Must be `"widget"` for web components. | | `wippy.tagName` | Yes | Custom element tag name. **Must contain a hyphen.** Use a namespaced prefix to avoid collisions: `orgname-component-name`. | | `wippy.props` | Yes | JSON Schema object describing component properties. Drives runtime attribute parsing, type coercion, and defaults. | | `wippy.events` | Recommended | JSON Schema describing custom events the component emits. Each key is the event name (kebab-case); the value schema describes `event.detail`. | **Package naming:** `@/widget-`. Examples: `@acme/widget-data-table`, `@myorg/widget-reaction-bar`. **Peer dependencies:** `vue` and `@wippy-fe/proxy` are provided by the host import map and must be in `peerDependencies` and marked `external` in `vite.config.ts`. `pinia` and `@iconify/vue` are also host-provided — add them if you use them directly. Never bundle `@wippy-fe/pinia-persist` as an external — it is not in the host import map and must be bundled. ### Props schema and attribute serialization HTML attributes are always strings. The `WippyElement` base class parses non-string props automatically based on the declared `type`: | JSON Schema type | Parsed from attribute as | |---|---| | `"string"` | Raw string value | | `"number"` | `parseFloat(attrValue)` | | `"boolean"` | `true` when attribute is present or `"true"`, `false` otherwise | | `"array"` / `"object"` | `JSON.parse(attrValue)` | Kebab-case attribute names (`allow-multiple`, `max-items`) are converted to camelCase (`allowMultiple`, `maxItems`) in the parsed props object. ## `src/index.ts` — element class and registration The entry point defines a `WippyVueElement` subclass and calls `define()` to register it. ```typescript import { WippyVueElement, define } from '@wippy-fe/webcomponent-vue' import type { WippyElementConfig, WippyPropsSchema } from '@wippy-fe/webcomponent-vue' import type { ComponentProps } from './types.ts' import type { Events } from './constants.ts' import MyWidget from './app/my-widget.vue' import stylesText from './styles.css?inline' import pkg from '../package.json' class MyWidgetElement extends WippyVueElement { static get wippyConfig(): WippyElementConfig { return { propsSchema: pkg.wippy.props as WippyPropsSchema, hostCssKeys: ['themeConfigUrl'] as const, inlineCss: stylesText, } } static get vueConfig() { return { rootComponent: MyWidget, } } } // Required export — the host calls this to get the element class, // then calls customElements.define(tagName, class) itself. export async function webComponent() { return MyWidgetElement } // Self-registration when loaded with ?declare-tag= in the URL. // define() inspects import.meta.url and calls customElements.define() // if that search param is present. define(import.meta.url, MyWidgetElement) ``` ### `wippyConfig` fields | Field | Type | Description | |---|---|---| | `propsSchema` | `WippyPropsSchema` | The `wippy.props` JSON Schema object from `package.json`. Drives attribute parsing. | | `hostCssKeys` | `readonly string[]` | CSS URLs to request from the host and inject into the shadow root. See the [hostCssKeys reference](#hostcsskeys-reference) below. | | `inlineCss` | `string` | Component's own compiled CSS, loaded via `?inline` Vite import. Injected into the shadow root as a `