Subchapter 2.81
references/SITE_PLUGIN.mdMarkdown13 KBView on GitHub
Site plugins are custom elements that integrate into predefined slots within Wix business solutions (Wix Stores, Wix Bookings, Wix eCommerce, etc.), extending their functionality and user experience. Site owners place site plugins into UI slots using the plugin explorer in Wix editors.
Use wix generate --params with extensionType: SITE_PLUGIN. slotId is <componentId>:<slotId> — the colon-joined widget component ID and slot ID. Run wix schema generate --type SITE_PLUGIN to list the available slotId values (each anyOf entry has the slot’s human-readable title). The CLI generates the folder, the plugin .tsx, the settings panel .tsx, the builder file, the UUID, the logo SVG, and the src/extensions.ts registration.
Site plugins consist of three required files generated by the CLI:
Custom element component that renders in the slot using native HTMLElement:
HTMLElement classobservedAttributes for reactive propertiesconnectedCallback() and attributeChangedCallback() for renderingdisplay-name)Settings panel shown in the Wix Editor sidebar:
@wix/design-system) components — see the wix-design-system skill for component reference@wix/editor widget APIwidget.getProp('kebab-case-name')widget.setProp('kebab-case-name', value)WixDesignSystemProvider > SidePanel > SidePanel.ContentGenerated by the CLI with placements, tagName, element/settings paths, marketData (name/description/logoUrl), and installation.autoAdd. Edit marketData.description and installation.autoAdd directly in this file after scaffolding if defaults don’t match your needs. marketData.description must stay under 130 characters.
Site plugins use native HTMLElement custom elements:
// my-site-plugin.tsx
class MyElement extends HTMLElement {
static get observedAttributes() {
return ['display-name'];
}
constructor() {
super();
}
connectedCallback() {
this.render();
}
attributeChangedCallback() {
this.render();
}
render() {
const displayName = this.getAttribute('display-name') || "Your Plugin's Title";
this.innerHTML = `
<div style="font-size: 16px; padding: 16px; border: 1px solid #ccc; border-radius: 8px; margin: 16px;">
<h2>${displayName}</h2>
<hr />
<p>
This is a Site Plugin generated by Wix CLI.<br />
Edit your element's code to change this text.
</p>
</div>
`;
}
}
export default MyElement;Key Points:
HTMLElement class directlyobservedAttributes static getter to list reactive attributesdisplay-name, bg-color)connectedCallback() for initial renderattributeChangedCallback() to re-render when attributes changethis.getAttribute('attribute-name') to read attribute valuesdefine() for you — do NOT call customElements.define() in your code// my-site-plugin.panel.tsx
import React, { type FC, useState, useEffect, useCallback } from 'react';
import { widget } from '@wix/editor';
import {
SidePanel,
WixDesignSystemProvider,
Input,
FormField,
} from '@wix/design-system';
import '@wix/design-system/styles.global.css';
const Panel: FC = () => {
const [displayName, setDisplayName] = useState<string>('');
useEffect(() => {
widget.getProp('display-name')
.then(displayName => setDisplayName(displayName || "Your Plugin's Title"))
.catch(error => console.error('Failed to fetch display-name:', error));
}, [setDisplayName]);
const handleDisplayNameChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
const newDisplayName = event.target.value;
setDisplayName(newDisplayName);
widget.setProp('display-name', newDisplayName);
}, [setDisplayName]);
return (
<WixDesignSystemProvider>
<SidePanel width="300" height="100vh">
<SidePanel.Content noPadding stretchVertically>
<SidePanel.Field>
<FormField label="Display Name">
<Input
type="text"
value={displayName}
onChange={handleDisplayNameChange}
aria-label="Display Name"
/>
</FormField>
</SidePanel.Field>
</SidePanel.Content>
</SidePanel>
</WixDesignSystemProvider>
);
};
export default Panel;Key Points:
widget.getProp() and widget.setProp() use kebab-case (e.g., "display-name")WixDesignSystemProvider > SidePanel > SidePanel.Content@wix/design-system@wix/design-system/styles.global.css for stylesaria-label for accessibilitySite plugin settings panels can use inputs.selectColor() and inputs.selectFont() from @wix/editor to open the native Wix Editor color and font picker dialogs.
Opens the Wix color picker with theme colors, gradients, and more — NOT a basic HTML <input type="color">.
import React, { type FC } from 'react';
import { inputs } from '@wix/editor';
import { FormField, Box, FillPreview, SidePanel } from '@wix/design-system';
interface ColorPickerFieldProps {
label: string;
value: string;
onChange: (value: string) => void;
}
export const ColorPickerField: FC<ColorPickerFieldProps> = ({
label,
value,
onChange,
}) => (
<SidePanel.Field>
<FormField label={label}>
<Box width="30px" height="30px">
<FillPreview
fill={value}
onClick={() => inputs.selectColor(value, { onChange: (val) => { if (val) onChange(val); } })}
/>
</Box>
</FormField>
</SidePanel.Field>
);Opens the Wix font picker with font family, size, bold, italic, and other typography features.
import React, { type FC } from 'react';
import { inputs } from '@wix/editor';
import { FormField, Button, Text, SidePanel } from '@wix/design-system';
interface FontValue {
font: string;
textDecoration: string;
}
interface FontPickerFieldProps {
label: string;
value: FontValue;
onChange: (value: FontValue) => void;
}
export const FontPickerField: FC<FontPickerFieldProps> = ({
label,
value,
onChange,
}) => (
<SidePanel.Field>
<FormField label={label}>
<Button
size="small"
priority="secondary"
onClick={() => inputs.selectFont(value, { onChange: (val) => onChange({ font: val.font, textDecoration: val.textDecoration || "" }) })}
fullWidth
>
<Text size="small" ellipsis>Change Font</Text>
</Button>
</FormField>
</SidePanel.Field>
);Important:
inputs.selectColor() from @wix/editor with FillPreview — do NOT use <Input type="color">inputs.selectFont() from @wix/editor with the callback pattern inputs.selectFont(value, { onChange })inputs from @wix/editor (not from @wix/sdk)Site plugins use kebab-case consistently for HTML attributes:
| File | Convention | Example |
|---|---|---|
<plugin>.tsx (getAttribute) | kebab-case | this.getAttribute('display-name') |
<plugin>.tsx (observedAttributes) | kebab-case | ['display-name', 'bg-color'] |
<plugin>.panel.tsx (widget API) | kebab-case | widget.getProp('display-name') |
| Topic | Reference |
|---|---|
| Complete Examples | EXAMPLES.md |
| Slots (App IDs, runtime APIs, design guidelines, multiple placements) | SLOTS.md — run wix schema generate --type SITE_PLUGIN for the authoritative slotId enum |
| WDS Components | the wix-design-system skill |
Site plugins integrate into predefined slots in Wix business solutions. Each slot is identified by:
Common placement areas include product pages (Wix Stores), checkout and side cart (Wix eCommerce), booking pages (Wix Bookings), service pages, event pages, and blog post pages.
For App Definition IDs, per-slot runtime APIs, design guidelines, and placement constraints, see SLOTS.md. Run wix schema generate --type SITE_PLUGIN for the authoritative slotId enum.
If you are building a plugin for the checkout page, it may not support automatic addition upon installation. You must create a dashboard page to provide users with a way to add the plugin to their site. See EXAMPLES.md for the dashboard page pattern.
For complete examples with all three required files (plugin component, settings panel, extension configuration), see EXAMPLES.md.
Example use cases:
define() - Wix handles customElements.define() for you automaticallySite plugins are sandboxed when rendered in the editor. This means they’re treated as if they come from a different domain, which impacts access to browser storage APIs.
Restricted APIs in the editor:
localStorage and sessionStorage (Web Storage API)document.cookie (Cookie Store API)How to handle sandboxing:
Use the viewMode() function from @wix/site-window to check the current mode before accessing restricted APIs:
import { window as wixWindow } from '@wix/site-window';
const viewMode = await wixWindow.viewMode();
if (viewMode === 'Site') {
const item = localStorage.getItem('myKey');
} else {
// Mock storage or modify API usage for editor mode
}Site plugins can import and use Wix SDK modules directly — you do NOT need createClient(). The Wix runtime provides the client context automatically.
// ✅ CORRECT — Import SDK modules directly
import { items } from "@wix/data";
import { currentCart } from "@wix/ecom";
import { products } from "@wix/stores";
class MyPlugin extends HTMLElement {
async loadData() {
// Call SDK methods directly — no createClient needed
const result = await items.query("MyCollection").find();
const cart = await currentCart.getCurrentCart();
const productList = await products.queryProducts().limit(10).find();
}
}// ❌ WRONG — Do NOT use createClient in site plugins
import { createClient } from "@wix/sdk";
const wixClient = createClient({ modules: { items, products } });
await wixClient.items.query(...); // Wrong — API surface differs through client