Dynamic Parameters Management
Complete guide for managing dynamic parameters for embedded scripts in dashboard pages.
Description
This dashboard page manages dynamic parameters for an embedded script. The parameters are configurable values that site owners can set through this dashboard interface, and they will be passed to the embedded script as template variables.
IMPORTANT: Only implement UI for parameters that are relevant to your current use case. Ignore parameters that don't apply to the functionality you're building. It's perfectly fine to not use all parameters if they're not applicable.
Implementation Requirements
1. Import embeddedScripts
- Import embeddedScripts directly from '@wix/app-management'
- Use embeddedScripts.getEmbeddedScript() to load parameters
- Use embeddedScripts.embedScript({ parameters }) to save parameters
- Example:
import { embeddedScripts } from '@wix/app-management';
2. Type Definition
- Create a TypeScript type/interface that includes all the dynamic parameters
- Example:
export type MyScriptOptions = { headline: string; text: string; imageUrl: string; activationMode: 'active' | 'timed' | 'disabled'; startDate?: string; endDate?: string; };
3. State Management
- Use React useState to manage the parameter values locally
- Initialize with default values for all parameters
- Add separate state for isLoading and isSaving
- Use useEffect to load parameters on mount
- IMPORTANT: Parameters are returned as strings from the API, so you must handle type conversions:
- BOOLEAN parameters: Convert from string 'true'/'false' to boolean
- NUMBER parameters: Convert from string to number using Number()
- Other types: Use as-is
- Example:
const [options, setOptions] = useState<MyScriptOptions>(defaultOptions); const [isLoading, setIsLoading] = useState(true); const [isSaving, setIsSaving] = useState(false); useEffect(() => { const loadSettings = async () => { try { const embeddedScript = await embeddedScripts.getEmbeddedScript(); const data = embeddedScript.parameters as Partial<Record<keyof MyScriptOptions, string>> || {}; setOptions((prev) => ({ ...prev, textField: data?.textField || prev.textField, booleanField: data?.booleanField === 'true' ? true : data?.booleanField === 'false' ? false : prev.booleanField, numberField: Number(data?.numberField) || prev.numberField, })); } catch (error) { console.error('Failed to load settings:', error); } finally { setIsLoading(false); } }; loadSettings(); }, []);
4. Loading State
- Show a Loader component while isLoading is true
- Example:
{isLoading ? ( <Box align="center" verticalAlign="middle" height="50vh"> <Loader text="Loading..." /> </Box> ) : ( // ... form content )}
5. Form Components
- IMPORTANT: Only create form fields for parameters relevant to your use case
- Skip parameters that don't apply to the functionality being built
- Create appropriate WDS form fields based on parameter types:
- TEXT → Input component with FormField
- NUMBER → Input component with type="number"
- BOOLEAN → Checkbox or ToggleSwitch
- IMAGE → Custom ImagePicker component (see components/image-picker.tsx)
- DATE → DatePicker component
- SELECT → Dropdown component with options
- URL → Input with URL validation
- Use FormField wrapper for labels and validation messages
- Set required validation based on parameter.required flag
- Show validation errors using FormField status and statusMessage props
6. Save Functionality
- Add a Save button in the Page.Header actionsBar
- Make handleSave an async function
- CRITICAL: All parameters must be passed as STRING values because they are used as template variables in the embedded script
- Convert all values to strings before saving:
- BOOLEAN: Use String(value) or value.toString()
- NUMBER: Use String(value) or value.toString()
- Other types: Already strings, use as-is
- Disable the Save button if required fields are missing or while saving
- Add proper error handling
7. Form Validation
- Implement validation for required fields
- Show error states on FormField components
- Display clear error messages
8. Layout and Organization
- Use Card components to group related fields
- Use Box with direction="vertical" for form layout
- Add appropriate spacing with gap props
- Include helpful descriptions using Card subtitle or FormField infoContent
- Consider creating a separate settings component for complex forms
9. Preview Component (Optional but Recommended)
- If applicable, create a preview component that shows how the configuration will look
- Display the preview alongside the settings form using Layout and Cell components
- The preview should react to parameter changes in real-time
Files to Generate
When dynamic parameters are present, generate all of these — the two wrapper files are not optional:
| File | Role |
|---|---|
dashboard/BusinessManagerTheme.tsx |
Theme wrapper — BUSINESS_MANAGER_THEME.md § 2 |
dashboard/withProviders.tsx |
Provider wrapper (below) |
dashboard/pages/page.tsx |
The page, exported wrapped in withProviders |
dashboard/types.ts |
Parameter type definitions |
| Component files | Settings forms, previews |
Parameters are saved as individual string fields, never as one JSON string, and converted back to their real types on load. Use embeddedScripts directly from @wix/app-management.
Provider Wrapper Implementation
Generate src/extensions/dashboard/withProviders.tsx:
import React from 'react';
import { i18n } from '@wix/essentials';
import { BusinessManagerTheme } from './BusinessManagerTheme';
export default function withProviders<P extends {} = {}>(Component: React.FC<P>) {
return function DashboardProviders(props: P) {
return (
<BusinessManagerTheme locale={i18n.getLocale()}>
<Component {...props} />
</BusinessManagerTheme>
);
};
}
// Also export as named export for backwards compatibility
export { withProviders };Business Manager passes none of the redesign through the extension's iframe, so a wrapper holding WixDesignSystemProvider alone renders the pre-redesign look while compiling and previewing cleanly. features={{ newColorsBranding: true }} predates the theme and does not substitute for it.
Using Provider Wrapper
In your dashboard page component (page.tsx):
import withProviders from '../../withProviders';- Import embeddedScripts from '@wix/app-management'
- Add no design-system providers in the page —
withProvidersowns them - Export wrapped:
export default withProviders(MyComponent); - The component holds the Page and its content, not providers
Example structure:
import { useEffect, useState, type FC } from 'react';
import { dashboard } from '@wix/dashboard';
import { embeddedScripts } from '@wix/app-management';
import { Page, Card, Button, ... } from '@wix/design-system';
import withProviders from '../../withProviders'; // owns the stylesheets and providers
const MyDashboardPage: FC = () => {
const [options, setOptions] = useState<MyScriptOptions>(defaultOptions);
const [isLoading, setIsLoading] = useState(true);
const [isSaving, setIsSaving] = useState(false);
useEffect(() => {
const loadSettings = async () => {
try {
const embeddedScript = await embeddedScripts.getEmbeddedScript();
const data = embeddedScript.parameters || {};
// ... update options with data
} catch (error) {
console.error('Failed to load settings:', error);
} finally {
setIsLoading(false);
}
};
loadSettings();
}, []);
const handleSave = async () => {
setIsSaving(true);
try {
await embeddedScripts.embedScript({ parameters: { /* ... */ } });
dashboard.showToast({ message: 'Saved!', type: 'success' });
} catch (error) {
console.error('Failed to save:', error);
dashboard.showToast({ message: 'Failed to save', type: 'error' });
} finally {
setIsSaving(false);
}
};
return (
<Page height="100vh">
{/* Page content - NO WixDesignSystemProvider here */}
</Page>
);
};
export default withProviders(MyDashboardPage);Critical Notes
- Only implement UI for parameters that are relevant to your specific use case - ignore parameters that don't apply
- ALWAYS generate
BusinessManagerTheme.tsxandwithProviders.tsx, and wrap the page export withwithProviders()— no design-system providers in the page itself - ALWAYS use embeddedScripts directly from '@wix/app-management'
- ALWAYS convert parameter values to strings when saving (embeddedScripts.embedScript must receive all string values in the parameters object)
- ALWAYS convert string parameters back to proper types when loading (e.g., 'true' -> true for booleans, string to number for numbers)
- ALWAYS handle the loading state with isLoading state variable
- ALWAYS handle the saving state with isSaving state variable
- ALWAYS add try/catch blocks for async operations (loading and saving)
- ALWAYS use async/await for embeddedScripts operations
- ALWAYS merge parameter values correctly in useEffect with proper type conversions
- ALWAYS validate required fields and show appropriate error states
- The parameter keys MUST match exactly what is expected in the embedded script template variables
- Each parameter is saved as a separate field, NOT as a JSON string