> Portal Navigation:
> 
> - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version.
> - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages).
> - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`).
> - Top-level index of all portals: https://dev.wix.com/docs/llms.txt
> - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt

## Resource: Custom Element Site Widget Extension Files and Code

## Article: Custom Element Site Widget Extension Files and Code

## Article Link: https://dev.wix.com/docs/site-extensions/custom-elements/custom-element-site-widget-extension-files-and-code.md

## Article Content:

# Custom Element Site Widget Extension Files and Code

A [custom element site widget](https://dev.wix.com/docs/overview/extensions-framework/site-extensions/site-widgets.md) in a Wix CLI app is a custom HTML element plus a settings panel.

> **Note:** A Studio Two site doesn't use this extension. Headless projects don't support it. See [About Custom Element Site Widgets](https://dev.wix.com/docs/site-extensions/custom-elements/about-custom-element-site-widgets.md).

When you add the extension, the generated files land under `src/extensions/site/widgets/<widget-folder>/`. **Generate** also adds a default thumbnail at `public/thumb.png`, and registers the extension in your project's `extensions.ts`.

```text
public/
└── thumb.png
src/
└── extensions/
    └── site/
        └── widgets/
            └── <widget-folder>/
                ├── <widget-folder>.extension.ts
                ├── <widget-folder>.tsx
                ├── <widget-folder>.panel.tsx
                └── <widget-folder>.module.css
```

| File | What it's for |
|------|---------------|
| `<widget-folder>.extension.ts` | The [configuration file](#configuration-file) for the widget ID, size, installation, tag name, and file paths. |
| `<widget-folder>.tsx` | The [element](#element). This file exports the custom element. |
| `<widget-folder>.panel.tsx` | The [settings panel](#settings-panel) that opens from the widget's action bar in the editor. |
| `<widget-folder>.module.css` | CSS Module styles for the element. |
| `public/thumb.png` | The default [thumbnail](#thumbnail) for the **Add** panel and the Manage Apps page. The generated `presets` entry points at this file. |

The CLI calls `define()` for the custom element. The element file exports the class or the converted React component. It doesn't call `define()` itself.

## Configuration file

The `<widget-folder>.extension.ts` file declares the widget with `extensions.customElement()`.

```ts
import { extensions } from '@wix/astro/builders';

export default extensions.customElement({
  id: '9ff1170b-550b-4f14-bda9-2ead587d9c47',
  name: 'My Element',
  width: {
    defaultWidth: 450,
    allowStretch: true,
  },
  height: {
    defaultHeight: 250,
  },
  installation: {
    staticContainer: 'HOMEPAGE',
  },
  behaviors: {
    dashboard: {
      dashboardPageComponentId: '154f642e-a705-4ff3-8421-6119354a3384',
    },
  },
  tagName: 'my-element',
  element: './extensions/site/widgets/my-element/my-element.tsx',
  settings: './extensions/site/widgets/my-element/my-element.panel.tsx',
  presets: [
    {
      id: '44e6714f-f81b-4824-ab77-50037439b881',
      name: 'My Preset',
      thumbnailUrl: '{{BASE_URL}}/public/thumb.png',
    },
  ],
});
```

Keep this file. If you add the extension yourself, include it.

### Configuration fields

| Field | Type | Description |
|--|--|--|
| `id` | string | Widget ID as a GUID. It must be unique across extensions in the project. **Generate** sets it. |
| `name` | string | Display name in the app dashboard. |
| `width.defaultWidth` | number | Width in pixels when the widget is first added to a site. |
| `width.allowStretch` | boolean | Whether the Wix user can toggle the widget between full width and the default width. |
| `width.stretchByDefault` | boolean | Whether the widget stretches to full width when it is first added. |
| `height.defaultHeight` | number | Height in pixels when the widget is first added to a site. |
| `installation.staticContainer` | string | Where the widget is added when the app is installed. `"HOMEPAGE"` adds it to the site home page. Omit the field and the widget is not added on install. Wix users then add it from the **Add** panel. In the CLI, automatic installation applies only to the home page. `installation.autoAdd` is deprecated. Use `installation.staticContainer`. |
| `installation.essential` | boolean | Whether the widget is essential to the app. Set this only when the widget is part of a site page. An essential widget can be added once, and deleting it removes the app from the site. |
| `tagName` | string | Custom element tag name. Use lowercase, start with a letter, and include at least one hyphen, such as `my-element`. The CLI registers this tag. |
| `element` | string | Path to the element file, relative to the project's `src/` directory. |
| `settings` | string | Path to the settings panel file, relative to the project's `src/` directory. |
| `behaviors.dashboard.dashboardPageComponentId` | string | ID of a dashboard page extension. When set, the widget's action bar includes a **Manage** button that opens that page. |
| `presets` | array | Thumbnail shown in the **Add** panel and on the Manage Apps page. Each entry has an `id` GUID, a `name`, and a `thumbnailUrl`. |

### Thumbnail

**Generate** creates `public/thumb.png` and sets `presets[0].thumbnailUrl` to `{{BASE_URL}}/public/thumb.png`. `{{BASE_URL}}` resolves to the project's `public/` folder.

Replace `public/thumb.png` with your own image, or add another file under `public/` and point `thumbnailUrl` at it. If you rename the image, update `thumbnailUrl`. Without a thumbnail, the widget doesn't appear in the **Add** panel or on the Manage Apps page. See [Display a Custom Element Site Widget in the Add Elements Panel](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/site-widgets/display-a-site-widget-in-the-add-panel-and-site-dashboard.md) in Build Apps.

## Element

The `<widget-folder>.tsx` file exports the custom element. You can write the UI in other files and compose it here. This file is the one the CLI loads.

The generated file uses React and converts the component with `reactToWebComponent()`. Each prop in the `props` option maps to a kebab-case attribute. The settings panel writes that attribute with `widget.setProp()`, and the component receives the value as the prop.

```tsx
import React, { type FC } from 'react';
import ReactDOM from 'react-dom';
import reactToWebComponent from 'react-to-webcomponent';

interface Props {
  displayName?: string;
}

const CustomElement: FC<Props> = ({ displayName = 'Your widget title' }) => {
  return <h2>{displayName}</h2>;
};

const customElement = reactToWebComponent(CustomElement, React, ReactDOM as any, {
  props: {
    displayName: 'string',
  },
});

export default customElement;
```

You can export a class that extends `HTMLElement` instead. List each attribute the element responds to in `observedAttributes`, and update the element in `attributeChangedCallback()`. The browser calls `attributeChangedCallback()` only for attributes in that list.

```tsx
class CustomElement extends HTMLElement {
  static get observedAttributes() {
    return ['display-name'];
  }

  connectedCallback() {
    this.render();
  }

  attributeChangedCallback() {
    this.render();
  }

  render() {
    this.textContent = this.getAttribute('display-name') || 'Your widget title';
  }
}

export default CustomElement;
```

In either form, don't call `define()`. The CLI registers `tagName` for you.

## Settings panel

The `<widget-folder>.panel.tsx` file is a React component. It runs in the editor when the Wix user opens **Settings** on the widget's action bar. The panel must be React.

Read and write widget properties with `widget` from `@wix/editor`. Properties are bound to the custom element's attributes. `widget.setProp('display-name', value)` updates the `display-name` attribute, and the element re-renders from that attribute.

```tsx
import React, { type FC, useState, useEffect } from 'react';
import { widget } from '@wix/editor';

const Panel: FC = () => {
  const [displayName, setDisplayName] = useState('');

  useEffect(() => {
    widget.getProp('display-name').then((value) => {
      setDisplayName(value || 'Your widget title');
    });
  }, []);

  return (
    <input
      value={displayName}
      onChange={(event) => {
        const next = event.target.value;
        setDisplayName(next);
        widget.setProp('display-name', next);
      }}
    />
  );
};

export default Panel;
```

For a React element, add the prop to the component and to the `props` option, then call `widget.setProp()` with the kebab-case name. For an `HTMLElement` class, add the attribute to `observedAttributes`.

When the panel writes several properties, write them one at a time and confirm each value with `widget.getProp()` before the next write. For a full panel, see [Create a Settings Panel for a Site Widget or Plugin](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/site-extensions/create-a-settings-panel-for-a-site-widget-or-plugin-wix-cli-and-self-hosting.md) in Build Apps.

## Registration

A Wix CLI project registers the widget in `src/extensions.ts`. **Generate** adds the import and the `app().use()` call:

```ts
import { app } from '@wix/astro/builders';
import myElement from './extensions/site/widgets/my-element/my-element.extension.ts';

export default app().use(myElement);
```

When the project already has other extensions, **Generate** appends another `.use()` call to the existing chain.

## See also

- [About Custom Element Site Widgets](https://dev.wix.com/docs/site-extensions/custom-elements/about-custom-element-site-widgets.md)
- [Site Widgets](https://dev.wix.com/docs/overview/extensions-framework/site-extensions/site-widgets.md)
- [Add a Custom Element Extension with the Wix CLI](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-the-cli/supported-extensions/site/custom-elements/add-a-custom-element-extension-with-the-wix-cli.md)
- [Widget API](https://dev.wix.com/docs/sdk/host-modules/editor/widget/introduction.md)