An Editor React Component (ERC) is built component first. You write the React code, the prop types, and the CSS, and then you regenerate the editorElement portion of the manifest from those three files. What a Wix user can select, edit, and style in the editor follows from how you write them.
This article walks through that loop end to end, from a scaffolded component to one with its own panels, presets, and states. The examples build a testimonial card with a quote and an author name.
Follow the instructions below to:
Start from a generated ERC rather than an empty folder, so the file structure and registration are already in place. For the procedure, see Add an Editor React Component Extension in Studio Two.
You can find the new files under src/extensions/site/components/<component-folder>/. You spend most of this article in these files:
<component-folder>.tsx<component-folder>.props.ts<component-folder>.module.css<component-folder>.extension.tsYou also update component.preview.tsx once, in Step 3. Leave component.tsx as generated. For what every file in the folder is for, see Editor React Component Extension Files and Code.
To save a file, press Control+S (or Command+S on macOS). A circle on a file's tab means the file has unsaved edits. When the status bar shows Synced, the editor has your saved changes.
Tip: If the status bar shows that the sync failed, click it to retry. If it keeps syncing for a long time, reload the editor.
Write your JSX in <component-folder>.tsx. Two rules govern how the editor sees it.
First, the root element carries the id and className props Wix passes in, plus a plain string class that the manifest selector matches. Without both the id and a matching class, the component renders but a Wix user can't select it or open its panels. Name the root class after the component so it doesn't collide with anything else on the page.
Second, every element you want the editor to target needs a plain string class alongside its CSS Module class. CSS Modules rewrite class names at build time, so a module class alone never matches a manifest selector. Give structural wrappers a module class only, and the editor leaves them alone.
For the example, .testimonial-card is the root selector, and .testimonial-quote and .testimonial-author mark the inner elements. The scaffold uses clsx to combine class names, so the example does too:
For the full DOM contract, including selector length limits and the props Wix provides, see Component Requirements and Runtime Props Reference.
Declare your props type and a defaultProps constant in <component-folder>.props.ts. Each prop you declare becomes editable content in the editor, and the value a Wix user sets arrives back on the component as that same prop at runtime.
Set each default once, in this file. It feeds both the rendered component and the editor panels, so the controls open on the same values a Wix user sees on the canvas. A copy of the component that's already on a page keeps the values it was added with, so a changed default shows only on copies added after you save. Leave id and className out of defaultProps, because Wix always supplies them.
For the example, the props replace the scaffold's image, title, description, and direction:
When you rename or replace props, update component.preview.tsx to match. That file is what the editor canvas loads. The scaffold wraps the component in withFallbackPlaceholder with a requiredDataFields list that names the scaffold's title and description props. If a field in that list is missing from the component's props, the canvas shows a placeholder instead of the component.
List your own content props in requiredDataFields, not id or className. For the example:
Write default styles in <component-folder>.module.css. CSS Modules scope the class names to your component, so they can't collide with other components on the page.
The CSS properties you set here are what the editor turns into design controls. A property you never style isn't offered to Wix users.
For the example:
These scoped class names aren't what the manifest matches. Keep the plain string classes from Step 2 on every element the editor targets.
The editorElement in <component-folder>.generated.ts is the part of the manifest that declares editable data, inner elements, CSS properties, presets, states, and layout. The editor builds its panels from it. After you change the component, its props, or its CSS, regenerate the manifest.
To regenerate the manifest:
In the Code tab, open the terminal. Press Control+J (or Command+J on macOS).
Run:
Then open <component-folder>.generated.ts and read it as a report on what you wrote:
data. A Wix user edits text props in the component's Edit Text panel.elements. Generated entries set inlineElement.behaviors.selectable to false, so a Wix user can't select those elements on their own. Step 6 covers changing that.cssProperties entry that the editor can offer as a design control.If something you expected is missing, fix it in the source files and regenerate. A control that doesn't appear usually means a prop wasn't declared or a property was never styled. An element missing from elements usually has a module class but no plain string class.
Don't edit <component-folder>.generated.ts. Your changes are overwritten the next time you regenerate the manifest.
For what each manifest property controls in the editor, see About Auto Panels for Editor React Components.
<component-folder>.extension.ts is the manifest you own. Regenerating never overwrites it, so this is where you adjust anything the generated editorElement got wrong or left bare.
The scaffold merges your overrides onto the generated editorElement with deepmerge. Add each override to the second object, which already holds the scaffold's layout settings. You only write the values you change:
Common refinements:
displayName on data items, CSS properties, and elements so panels read in your words instead of your variable names.elements, use the element's key from the generated file and set inlineElement.behaviors.selectable to true. The element then gets its own action bar and panels. For every element field, see Elements.displayGroups to collect related properties into named sections inside a panel.displayFilters to hide a control unless another data item, preset, or state calls for it.layout for resize and stretch behavior, and installation.initialSize for the size the component takes when a Wix user first adds it to a page.This file also holds the component's identity and its resources. For every field, see About the Editor React Component Manifest.
Important: Some type properties are immutable once the manifest is saved for the first time, including dataType on a data item and elementType on an element. Changing one later fails validation and blocks deployment. To use a different type, add a new variable with the type you want instead of editing the existing one.
Presets and states are the two things a Wix user gets that don't come from your base declarations. You declare both in editorElement, inside the object you pass to deepmerge() in <component-folder>.extension.ts, as in Step 6, so regenerating the manifest never overwrites them. Each one also needs a small change in the component and its CSS. The sections below show what to add to which file.
A preset is a named visual variation of your component, such as a card layout and a minimal layout. Declaring presets gives the component an Explore Designs panel, where switching variation is a single click. To open it, select the component on the canvas and click the Explore Designs icon in the bottom action bar, to the left of the state selector.
1. <component-folder>.tsx: Wrap the root element in a container and spread wix.presetsWrapperProps onto it. Keep id and className on the inner element, not on the wrapper.
2. <component-folder>.extension.ts: Add presets inside the object you pass to deepmerge(), and set installation.presets to choose the starting preset at each breakpoint. Each preset needs a thumbnailUrl for the panel and a presetCssUrl for the CSS the editor applies when it's selected.
Replace the example URLs with your own. The thumbnail and CSS URLs must be publicly reachable.
To change a value when a Wix user picks a preset, use one of two fields. Use presetDefaults for the values that apply when a Wix user switches to the preset, such as layout behavior, CSS property defaults, and nested element defaults. Use styleOverrides when a value has to change by breakpoint or by state, such as a smaller font size on mobile. In the example, selecting Minimal in Explore Designs changes the card's background to #fff3cd.
Important: Every CSS property or element key you reference in presetDefaults or styleOverrides must already exist in the component's editorElement. If one doesn't, the editor reports "One or more referenced keys do not exist" under Site code errors, and the component stops loading on the canvas. Check the key names in <component-folder>.generated.ts first. For example, a background-color declaration in your CSS appears there as background, so backgroundColor isn't a valid key.
For every preset field and worked examples, see Presets.
A state is a condition a Wix user styles separately, such as hover or loading. Declaring states adds a state selector to the bottom action bar. To find it, select the component on the canvas. The selector is the drop-down that reads Regular, next to Edit Text. Choosing a state, such as Hover, shows the component in that state on the canvas, and the design controls you change apply to that state.
1. <component-folder>.extension.ts: Add states inside the object you pass to deepmerge(). How you declare each state depends on where it comes from:
pseudoClass for states the browser handles on the live site, such as hover or disabled. Set it to a NATIVE_STATE_TYPE value, imported from @wix/react-component-schema. A plain string such as 'hover' fails type checking.className for custom states with no pseudo-class equivalent, such as loading or selected. The editor applies this class to the component while the state is selected.props to put the component into that state on the editor canvas, so a Wix user can see what they're styling.2. <component-folder>.module.css: Write the styles for the state. Target both the pseudo-class, for the live site, and the state's class, for the editor canvas. Because CSS Modules rewrite class names, wrap the state class in :global().
3. (Optional) <component-folder>.extension.ts, on a CSS property: To give a property a different default under a state, add statesDefaultValues to that property in the object you pass to deepmerge(). Use the property's key from <component-folder>.generated.ts, and key each value by the state's name.
When a Wix user selects Hover in the state selector, the background control then starts from #e8f0fe instead of the regular value.
For every state field and worked examples, see States.
To check your component:
requiredDataFields list from Step 3, then regenerate the manifest as in Step 5.Panels that don't look right point back to a specific file. Missing or misnamed controls are a manifest matter, so return to Step 5 or Step 6. Controls that exist but don't change anything on the canvas usually mean a selector doesn't match a class the component renders, so return to Step 2.
When the component behaves the way you want, click Publish to ship it with the site.
Last updated: 2 November 2026