Subchapter 2.34
references/DASHBOARD_MODAL.mdMarkdown4 KBView on GitHub
Dashboard modals are popup dialogs triggered from dashboard pages or plugins. They use the Dashboard SDK for lifecycle control via openModal() and closeModal().
Use wix generate --params with all required fields:
wix generate --params '{"extensionType":"DASHBOARD_MODAL","title":"<title>","folder":"<folder>"}'| Field | Constraint |
|---|---|
title | Display name for the modal. |
folder | Lowercase alphanumeric and hyphens. |
The CLI generates the folder, the modal .tsx, the config file, the builder file, the UUID, and the src/extensions.ts registration. After scaffolding, implement the modal UI in the generated .tsx.
| Task | Method | Example |
|---|---|---|
| Open modal | dashboard.openModal() | openModal({ modalId: "modal-id" }) |
| Pass data to modal | params in openModal() | params: { userId: "123" } |
| Read data in modal | observeState() | dashboard.observeState((state) => { ... }) |
| Close modal | dashboard.closeModal() | closeModal() |
| Return data to parent | Pass data to closeModal() | closeModal({ ... }) |
| Wait for modal close | modalClosed Promise | const { modalClosed } = openModal(...); |
import { dashboard } from "@wix/dashboard";
// Simple open
const result = await dashboard.openModal({
modalId: "your-modal-id", // The id generated by the CLI for this modal
});
// Pass data to modal via params
const result = await dashboard.openModal({
modalId: "your-modal-id",
params: {
userId: user.id,
itemData: complexObject, // Objects are passed directly, no encoding needed
},
});
// Get notified when the modal is closed
const { modalClosed } = dashboard.openModal({
modalId: "your-modal-id",
});
const result = await modalClosed; // Resolves with data from closeModal()Inside the modal, subscribe via dashboard.observeState() to access whatever was passed in openModal({ params }). The callback receives the params object as state:
import { dashboard } from "@wix/dashboard";
dashboard.observeState((state) => {
// state contains the keys you passed in `openModal({ params: { ... } })`
console.log(state.userId, state.itemData);
});Call it inside a useEffect if you want to set local React state from the params.
Call closeModal() from within the modal extension. The optional argument is data passed back to the opener (resolved via modalClosed).
import { dashboard } from "@wix/dashboard";
dashboard.closeModal({ saved: true, itemId: "123" }); // arg is optionalThe argument must be cloneable via the structured clone algorithm (opens in a new tab) — strings, numbers, booleans, plain objects, arrays, Dates, Maps, Sets, ArrayBuffers. Not supported: functions, DOM nodes, class instances with methods, Symbols, Promises.
Edit <modal>.config.ts (generated alongside the modal) to change the title and dimensions. The generated .tsx already imports and uses it.
// <modal>.config.ts
export default {
title: 'User Settings',
width: 600,
height: 500,
};| Mistake | Fix |
|---|---|
| Can’t find modal ID | Check the modal’s generated builder file (extensions.ts) id field |
Using extensionId instead of modalId | Use modalId in openModal() |
| Can’t access params in modal | Use dashboard.observeState() to read passed data |
| Modal won’t close | Use dashboard.closeModal() from @wix/dashboard |
End-to-end edit-item flow. The scaffolded <modal>.tsx already wires up CustomModalLayout — the unique parts are on the opener side, in observeState, and in the save handler.
// Dashboard Page: open the modal with the item to edit
const handleEdit = (item: Item) => {
dashboard.openModal({
modalId: "edit-item-modal-guid",
params: { item }, // objects are passed directly via params
});
};
// Modal: read params, save, toast, close
const [formData, setFormData] = useState<Item | null>(null);
useEffect(() => {
dashboard.observeState((state) => {
if (state.item) setFormData(state.item);
});
}, []);
const handleSave = async () => {
// ...your save logic...
dashboard.showToast({ message: "Saved!", type: "success" });
dashboard.closeModal();
};