ProfessorFlow
Developer GuideIntermediate

Building High-Quality Custom Property Editors for Flow

The configuration UX is the first thing an admin sees of your component — and the part most authors ship last. Here's how to build a Custom Property Editor that admins actually enjoy using.

April 25, 202613 min
Hero image for Building High-Quality Custom Property Editors for Flow

When the Config Screen Decides Everything

You spent six weeks polishing the rendering. The component looks beautiful, the API is clean, the documentation is solid. An admin drags it onto a Flow screen, opens the configuration panel — and sees a textarea labeled Configuration JSON.

They close the tab.

This is the part most component authors get wrong. The component itself is half the deliverable. The other half is the configuration experience inside Flow Builder — the screen the admin actually touches. A great component with a bad Custom Property Editor (CPE) feels like a great car with a steering wheel made of rope. The mechanics work. Nobody wants to drive it.

Let's walk through how to build a CPE that earns the admin's trust in the first thirty seconds — and why most of that work isn't about features at all.

Chapter 01 · Foundations

What a Custom Property Editor Actually Is

Before we argue about UX, let's nail down the contract. A CPE is an LWC, you own the UI, Flow Builder owns the data — and there are exactly two flavors.

A Custom Property Editor is a Lightning Web Component that takes over the configuration panel for your action or screen component inside Flow Builder. Without one, Flow Builder generates a default form: one input per @api property, in declaration order, with no validation beyond data type. With one, you decide exactly what the admin sees, in what order, and under what conditions.

Flow Builder showing a configured Professor Flow Visual Picker custom property editor summary before opening the configuration modalBefore image: Flow Builder showing an auto-generated property panel for a screen component without a custom property editor
Default generated formCustom editor summary
Screenshot 01Drag the handle to compare Flow Builder's generated property form with a configured Visual Picker CPE summary. Same component slot, very different admin experience.

There are two flavors, and they're not interchangeable.

The two flavors:

FlavorTargetsUsed for
Action CPElightning__FlowActionEditorInvocable Apex actions — the configuration shown when an admin drops your @InvocableMethod into a Flow
Screen Component CPElightning__FlowScreenAdditionalPropertiesScreen components — extra configuration beyond the auto-generated property list
Table 01The two CPE targets. Pick the one that matches what your component is — they're not interchangeable.

You communicate with Flow Builder through a small contract: a getter/setter pair for inputVariables, an optional setter for genericTypeMappings, and a single custom event — configuration_editor_input_value_changed — that you fire whenever the admin changes something. That contract has not changed in years. What's changed is the room you have to work in.

Chapter 02 · Real Estate

Know Your Real Estate

The right-side panel is roughly 320 pixels wide. That's a hard constraint, not a styling suggestion. Most CPEs ignore it. The fix is structural.

The right-side configuration panel in Flow Builder is roughly 320 pixels wide. That's not a guess — it's a hard constraint your CPE inherits the moment it loads. A <lightning-input> field with a label takes about a third of the vertical space you have above the fold. A two-column layout barely fits. A data table doesn't fit at all.

This is where most CPEs fall apart: authors try to cram every configuration option into the panel because the panel is what they see during development. Then a real admin opens it and discovers a vertical canyon of inputs, scrolling for a minute, with no sense of where the important fields are.

The fix is structural, not visual: at a certain complexity threshold, stop fighting the panel and launch a modal.

Anti-pattern

Inline / Cramped

Every input crammed into 320px. Validation hidden below the fold. Admins scroll, get lost, and close the tab.

Configure Component
Component Label*
My Datatable
Source Records*
{!Account_Records}
Display Mode
Compact
Columns (JSON)*
[{"field":"Name"...
Sort Field
Sort Direction
ASC
Filter Expression
Page Size
25
Allow Selection
false
Allow Edit
false
14 more fields below
Columns (JSON) must be valid JSON
The right pattern

Modal Launcher

One labeled button. A summary line in human language. Open the modal only when there's work to do.

Configure Component
Component Label*
My Datatable
Datatable Configuration
Currently configured
  • 8 columns
  • Sorted by Name DESC
  • Filtered to active records
  • Inline edit enabled
Allow Multi-Select
false
Both panels are 320px wide. One drowns the admin in inputs.The other communicates state and gets out of the way.

The pattern on the right is straightforward: render a single button in the inline panel ("Configure datatable…"), and on click open an overlay using the lightning/overlayUtils module or a <c-fsc_modal>-style component. The modal is your UI — full-screen real estate, full-width tables, side-by-side column layouts, real space to breathe.

That single button should make the current configuration state legible at a glance. Show a tiny summary block — "8 columns, sorted by Name" — so the admin doesn't have to open the modal just to remember what they configured last week.

Keep it inline when…
  • The configuration is 3 or fewer simple inputs — name, a picklist, an optional toggle
  • All fields fit comfortably above the fold without scrolling
  • Admins will edit this component frequently — making them open a modal every time is friction
  • There's no preview, no table, no multi-column layout, no conditional sub-forms
Launch a modal when…
  • You have 4+ inputs, especially if any are conditional on other inputs
  • You need to show a preview, table, or column-mapping grid that needs real width
  • You're building a wizard with discrete steps — review, confirm, save
  • The configuration is rarely edited after initial setup — the modal cost amortizes over the component's lifetime
Chapter 03 · The Toolkit

Use Flow Resource Pickers, Not Plain Inputs

The single most common CPE mistake — and the one admins notice first — is using a plain text box for a value that could be a Flow resource.

Here's the scenario. Your action has a recordId input. You drop a <lightning-input type="text" label="Record ID" /> into your CPE. An admin tries to bind it to {!recordOutput.Id} from a previous Get Records element. They type the merge field syntax. Flow Builder saves it as a literal string. The Flow runs, the action receives the literal text {!recordOutput.Id}, and the integration silently fails.

A native Flow input would have offered them a resource picker — a dropdown that shows variables, formulas, constants, and merge fields available in the current Flow scope. Your CPE skipped that affordance, and the admin is in a debugging session that has nothing to do with their Flow logic.

lightning-flow-combobox

The standard option for picking a Flow resource. Shows variables, constants, formulas, and merge fields available in scope. Works for text, number, date, and reference inputs. Use this first before reaching for anything else.

Field-aware combobox

For inputs that should only accept a specific sObject field (e.g., a date column on the underlying record), filter the resource list by data type. The admin sees only fields that make sense, the impossible bindings just aren't shown.

Generic type pickers

When your action accepts a generic sObject (SObject not Account), use the genericTypeMappings system to let the admin pick the type first, then bind concrete fields. The CPE drives the type choice, the rest of the config conforms to it.

Community combobox

Many shops use c-fsc_flow-combobox from the UnofficialSF base pack. It predates the official lightning-flow-combobox, handles a few edge cases the official one still doesn't, and is the de-facto standard in older orgs. Either is fine — pick one and be consistent.

The rule of thumb: if the input could ever hold a Flow resource, give the admin a resource picker. A text input is for free-text labels and headings. Everything else — record IDs, field references, dynamic strings, numeric values that might be calculated — gets a picker. The five-minute investment to swap inputs becomes the difference between "this feels like a Salesforce action" and "this feels like a third-party form."

On guided UX

Six Principles

A CPE is not a settings page. It's a conversation between you and an admin trying to ship a Flow this afternoon. These are the six tells that separate a thoughtful CPE from a form that thinks it's documentation.

  1. Principle I

    Progressive disclosure

    Don't show every option at once. Start with the inputs that must be set, hide the rest behind an "Advanced" toggle or a second tab. The admin completes the basics, sees the component start working, and only opens the advanced section when they hit a real need.

  2. Principle II

    Smart defaults

    Every input should have the most common useful value prefilled. "Sort ascending" beats blank. A reasonable date format beats no format. The admin's job is to override defaults that don't fit, not to invent values from scratch.

  3. Principle III

    Contextual help

    Help text belongs next to the field it explains, not in a footnote, not in external documentation. Use the field-level-help slot of <lightning-input> for short hints. Use a small <lightning-helptext> icon for longer guidance.

  4. Principle IV

    Validation that teaches

    "Invalid input" is a failed conversation. "Field API names are case-sensitive — try Account.Name instead of account.name" is the same validation, written for a human. Your error messages are documentation that only fires when relevant.

  5. Principle V

    Show consequences

    When the admin picks an option that changes downstream behavior — say, switching from single-select to multi-select — render the consequence immediately. A small preview, a label change, a hint. Decisions made blind get reversed under support pressure.

  6. Principle VI

    Save state legibly

    A configured component should look configured. In the inline panel, show a one-line summary of the current state. In a modal, show a "what's changed" indicator so the admin knows whether their unsaved edits will stick.

The honest test: ask a coworker who has never seen your component to configure it for a realistic use case, without giving them any context. Watch what they hover, what they read, what they click first, where they pause. The first three places they pause are your highest-leverage UX bugs.

Chapter 04 · Patterns

A Pattern That Works: The Modal Wizard

For complex CPEs — anything with conditional inputs, mappings, or previews — the modal wizard is the most reliably good shape. Here's the recipe.

Configure Visual Picker modal showing a live preview beside the Data chapter and custom item setup form
Screenshot 02The Visual Picker CPE launches a real configuration workspace: source selection, live preview, editable items, and save/cancel controls in one focused modal.
  1. 1

    Inline panel: launcher + summary

    Render only two things in the right-side panel: a primary button (Configure datatable…) and a one-line summary of the current state (8 columns · Sorted by Name DESC · Filtered to active records). Nothing else. The panel's job is to communicate "this is configured, click here to change it."

  2. 2

    Modal: full-screen, sized for the work

    On click, open a modal at a comfortable size — typically 720–960px wide, autosize on tall content. Use lightning/overlayUtils for the official path, or a community modal LWC if your org standardizes on one. The modal owns the configuration UX entirely.

  3. 3

    Inside the modal: tabs or steps for grouping

    Group inputs by intent. Tabs work well for independent groups (Columns, Sorting, Filtering, Behavior). Steps work well for dependent groups where the admin can't pick step 3 without finishing step 1 (e.g., pick sObject → map fields → set defaults). Don't mix the two patterns in the same modal.

  4. 4

    Live validation, no submit-and-pray

    Validate as the admin types. Disable the Save button until the configuration is internally consistent. Show what's missing in plain language above the Save button: "Add at least one column to save." Submit-and-pray ("click Save, see what breaks") is the pattern admins associate with bad enterprise software.

  5. 5

    Save: fire the change event, close, update summary

    On Save, fire a single configuration_editor_input_value_changed event for each modified input. Close the modal. The inline panel's summary line updates to reflect the new state. The admin sees their decision reflected back without leaving Flow Builder.

  6. 6

    Cancel: discard cleanly

    Cancel must throw away the modal's local state and not fire any change events. Sounds obvious — half the CPEs out there fire a flurry of input-changed events on close because someone forgot to gate them behind the Save button. Test the cancel path explicitly.

This pattern scales. A datatable CPE, a row-action mapper, a multi-step picker for an Apex action — all the same shape. The shape becomes muscle memory both for you and for the admins who use multiple components from your library.

Chapter 05 · Anti-patterns

What Newton Sees Most Often

Five mistakes show up in nearly every CPE review. Each one is a five-minute fix. Each one decides whether an admin trusts your component.

Anti-pattern 01

Plain text inputs where a resource picker belongs

Don't

<lightning-input type="text" label="Record ID" /> for a value the admin will obviously want to bind to {!recordVar.Id}. The literal string saves, the Flow runs, the integration silently fails.

Do

<lightning-flow-combobox> (or the community c-fsc_flow-combobox) for any value that could hold a Flow resource. Plain inputs are reserved for free-text labels — period.

Anti-pattern 02

Showing API names instead of human labels

Don't

A field labeled enableMultiSelectMode__c. A picklist value of OPT_A. These are labels for you, the developer — they leak straight into the admin's face.

Do

Translate every label that crosses the developer/admin boundary. "Allow multiple selections." "Detailed view." Names admins can read aloud without flinching.

Anti-pattern 03

Required fields with no visual marker

Don't

Validation that fires only on Save and points at three fields the admin didn't know were mandatory. The fastest way to lose trust on the first interaction.

Do

Mark required fields with the asterisk and the required attribute on <lightning-input>. Validate as the admin types. Errors render under the offending field, not in a banner three sections away.

Anti-pattern 04

Modals that scroll horizontally

Don't

A horizontal scrollbar inside a modal. A table that overflows the dialog. Layouts that assume a 1600px monitor. All three are the same bug wearing different costumes.

Do

Constrain to 720–960px and scroll vertically when content grows. Tables get column groups, master-detail layouts, or pagination. Test at 1280px wide — if it breaks there, it'll break in production.

Anti-pattern 05

No state summary in the inline panel

Don't

After the admin closes the modal, the inline panel reverts to the same launcher button with no indication of what's configured. They re-open the modal every time, just to remember.

Do

Show a two-line summary in human language. "8 columns. Sorted by Name. Inline edit on." The cheapest UX upgrade you can ship — five lines of code, hours of admin time saved.

Chapter 06 · Ship It

A Pre-Flight Checklist Before You Ship

Before you tag a release of any component or action with a CPE, walk through this list. None of these are theoretical — every one comes from a real CPE that shipped and broke an admin's afternoon.

CheckWhat to verify
Resource pickers everywhere they belongNo <lightning-input> on a field that could accept a Flow variable, merge field, or formula
Required fields marked visuallyThe asterisk is visible, the validation fires before Save, the error message is human-readable
Smart defaults setEvery input that has a "common case" value comes prefilled with it
Help text in contextEvery non-obvious field has a one-line hint or a help icon — not in external docs
Modal threshold checkedIf the inline panel scrolls past one screen, you've crossed the threshold — refactor to a modal
Cancel path testedClosing the modal without saving fires zero input-changed events and discards local state
Inline summary lineAfter save, the panel shows a one-line summary of the configured state in human language
Error states styledValidation errors render in destructive red with the exact field highlighted, not a generic banner
Light mode testedThe CPE renders in Flow Builder, which is light mode by default — no dark-mode-only assumptions
Real admin testedSomeone who has never seen your component configured it cold without context. You watched.
ChecklistRun this list before every release. The last item is the most-skipped and the highest-leverage.

The last item is the most-skipped and the highest-leverage. Every component author thinks their CPE is intuitive because they wrote it. The first cold-eyes test always surfaces a label that's confusing, a default that's wrong, an input order that's reversed. Schedule it before the package is signed.

What Changes With Summer '26

Summer '26 brings three small improvements that meaningfully change how you can structure CPEs for invocable actions specifically. None of them require a rewrite — they just give you better building blocks.

Per-input CPEs

Attach a Custom Property Editor to a single input, not the entire action. A picklist input can have its own picker; a JSON-shaped input can have its own visual builder. The action's main CPE stays simple; the complex inputs get their own focused UX.

Picklist inputs

Define an action input as a true picklist instead of a free-text string. The admin sees the constrained choices, no validation needed, no typo possible. Replaces the most common pattern of "string input + custom CPE just to render a dropdown."

Section headers

Add named section headers to the auto-generated configuration UI. Without writing a CPE at all, you can group inputs into "Connection," "Behavior," "Advanced," and so on. For simpler actions, this might let you skip a custom CPE entirely.

The honest implication: if you ship invocable actions, plan a v3.x release that adopts these. The configuration UX of every action you've shipped is about to be revisited, and admins will feel the difference.

When in Doubt, Borrow

You don't have to build every CPE pattern from scratch. The community has done a lot of the hard work, and the docs cover the contract precisely. Read three before you write your own. Keep the patterns that work. Skip the patterns that don't.

Worth borrowing

The Reference Shelf

Three resources to read before you write a single line of CPE code. The first one is the de-facto base pack. The second is the canonical example of a complex screen-component CPE. The third is the source of truth for the contract itself.

  1. Ref. 01Open-source library

    UnofficialSF Lightning Flow Components

    Alex Edelstein & community · github.com/UnofficialSF

    The base pack the entire Flow community builds on. Ships c-fsc_flow-combobox, c-fsc_modal, the row-action wrapper, and dozens of other Flow-aware primitives. If you're writing a non-trivial CPE, you're almost certainly importing one of these — even if you don't realize it yet. Browse the flow_screen_components folder to see how the maintainers structure their own CPEs.

  2. Ref. 02Reference CPE
  3. Ref. 03Official documentation

    Develop Custom Property Editors

    Salesforce LWC Developer Guide · developer.salesforce.com

    The mechanics: inputVariables, genericTypeMappings, the configuration_editor_input_value_changed event, and the two CPE targets. Skim the comparison page first to understand what a CPE actually replaces, then jump to the screen component and invocable action examples. Read once, bookmark forever — the contract barely changes.

End of references

The last thing worth saying: the admin who configures your component is the most important user you have. They decide whether your action survives the Flow review, whether your screen component goes into the org's standard pattern library, whether the next admin in the org learns to trust your work. The configuration UX is not the final 5% of polish. It's the first 30 seconds of the admin's relationship with your component.

Ship it like that.

Closing

The CPE Is the Component

A great Custom Property Editor is the sound of a thousand small decisions that an admin will never explicitly notice. The right field type. The smart default. The picker instead of the textbox. The modal instead of the cramped panel. The summary line that reminds them what they configured last Tuesday.

Get those decisions right and the admin opens your component, configures it in two minutes, and moves on. Get them wrong and they spend forty minutes wrestling with a form that thinks it's documentation.

The configuration UX is not the wrapper around your component. It is your component, as far as the admin is concerned. Build it like you'd want to use it.

— Chris, April 2026

Search

Search components, articles, and tags