Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Dynamic Form Builder — Salesforce LWC

A fully reusable Lightning Web Component that renders any form at runtime from a JSON/Custom Metadata configuration — no code changes needed to add, remove, or reconfigure fields.


DynamicFormBuilderExample

Quick Start (Home Page)

Use these 3 properties in Lightning App Builder to get started fast:

  1. Form Config Metadata Name: Contact_Intake_Form
  2. Form Config JSON: leave blank
  3. Target Object API Name: Contact

Then click Save and Activate.

If you want inline JSON instead:

  1. Fill Form Config JSON with your JSON payload.
  2. Leave Form Config Metadata Name blank.
  3. Keep Target Object API Name as Contact (or your target object).

Architecture

dynamicFormBuilder  (parent — orchestrator)
│   ├─ Loads config from Custom Metadata OR inline JSON
│   ├─ Manages form state, validation, submission
│   └─ Fires public events: fieldchange, formsubmit, formreset, formcancel
│
└── dynamicFormField  (child — field renderer)
        ├─ Renders the correct lightning-* component per field type
        └─ Reports validity up to parent via validate event

Apex: DynamicFormBuilderController
        ├─ getFormConfig()   — reads FormConfig__mdt + FormFieldConfig__mdt
        └─ submitFormData()  — dynamic SObject insert via Schema API

Custom Metadata:
        ├─ FormConfig__mdt        — one record per form
        └─ FormFieldConfig__mdt   — child records per field (or use Config_JSON__c blob)

Supported Field Types

Type Rendered As
text lightning-input type="text"
email lightning-input type="email"
phone lightning-input type="tel"
number lightning-input type="number"
currency lightning-input formatter="currency"
percent lightning-input formatter="percent-fixed"
date lightning-input type="date"
datetime lightning-input type="datetime"
checkbox lightning-input type="checkbox"
toggle lightning-input type="toggle"
textarea lightning-textarea
picklist lightning-combobox
multipicklist lightning-dual-listbox
radio lightning-radio-group
checkboxgroup lightning-checkbox-group
url lightning-input type="url"
password lightning-input type="password"
lookup lightning-record-picker
displaytext Static read-only text block
separator Section header divider

JSON Config Schema

{
  "title":       "My Form",
  "description": "Optional subtitle shown below the card header.",
  "columns":     2,
  "fields": [
    {
      "fieldName":       "LastName",
      "label":           "Last Name",
      "type":            "text",
      "required":        true,
      "placeholder":     "Enter last name",
      "helpText":        "Shown in the field tooltip.",
      "defaultValue":    "",
      "maxLength":       80,
      "minLength":       2,
      "pattern":         "[A-Za-z]+",
      "patternMessage":  "Letters only.",
      "requiredMessage": "Last name is required.",
      "disabled":        false,
      "readOnly":        false,
      "order":           1
    },
    {
      "fieldName": "Department",
      "label":     "Department",
      "type":      "picklist",
      "required":  false,
      "options": [
        { "label": "Engineering", "value": "Engineering" },
        { "label": "Sales",       "value": "Sales" }
      ],
      "order": 2
    },
    {
      "fieldName":    "AccountId",
      "label":        "Account",
      "type":         "lookup",
      "lookupObject": "Account",
      "required":     true,
      "order":        3
    }
  ]
}

Usage

Home/App Page Builder Parameters

Use these values when you drag dynamicFormBuilder onto a Home page or App page:

App Builder Property Recommended Value Notes
Form Config Metadata Name Contact_Intake_Form Use this for metadata-driven config from FormConfig__mdt.
Form Config JSON (leave blank) Only fill this when you want inline JSON.
Target Object API Name Contact Required if you want submit to create records.
Submit Button Label Submit Optional UI label.
Cancel Button Label Cancel Optional UI label.
Success Message Form submitted successfully! Toast and success state message.
Show Reset Button true or false Optional behavior toggle.
Show Cancel Button true or false Optional behavior toggle.

Rules:

  1. Provide either formConfigName or formConfigJson.
  2. If both are provided, inline JSON (formConfigJson) takes precedence.
  3. If targetObjectApiName is blank, submit will not insert a Salesforce record.

Option 1 — Custom Metadata (recommended for production)

  1. Deploy the metadata types.
  2. Create a FormConfig__mdt record with DeveloperName = Contact_Intake_Form and paste JSON into Config_JSON__c.
  3. Drop the component on a page:
<c-dynamic-form-builder
    form-config-name="Contact_Intake_Form"
    target-object-api-name="Contact"
    submit-label="Create Contact"
    show-reset-button>
</c-dynamic-form-builder>

Option 2 — Inline JSON (great for dynamic/parent-driven scenarios)

App Builder configuration for inline JSON:

  1. Set Form Config JSON to your full JSON payload.
  2. Leave Form Config Metadata Name blank.
  3. Set Target Object API Name (for example Contact) if you want record creation.

Example inline JSON payload:

{
  "title": "Contact Intake Form",
  "description": "Please fill in all required fields.",
  "columns": 2,
  "fields": [
    {
      "fieldName": "FirstName",
      "label": "First Name",
      "type": "text",
      "required": false,
      "placeholder": "Enter first name",
      "maxLength": 80,
      "order": 1
    },
    {
      "fieldName": "LastName",
      "label": "Last Name",
      "type": "text",
      "required": true,
      "placeholder": "Enter last name",
      "maxLength": 80,
      "order": 2
    },
    {
      "fieldName": "Email",
      "label": "Email Address",
      "type": "email",
      "required": true,
      "placeholder": "email@example.com",
      "order": 3
    }
  ]
}
<c-dynamic-form-builder
    form-config-json={myJsonString}
    onformsubmit={handleFormSubmit}>
</c-dynamic-form-builder>

Option 3 — Metadata-Only Child Records (no Config_JSON__c)

Use this when you want form structure assembled from FormFieldConfig__mdt child records instead of the JSON blob on FormConfig__mdt.

  1. Set Form Config Metadata Name to Contact_Metadata_Form.
  2. Leave Form Config JSON blank.
  3. Set Target Object API Name to Contact.

Option 4 — Flow Screen

Add the component as a Flow Screen element. It appears in the component palette because lightning__FlowScreen is declared in the meta XML.


Public @api Properties

Property Type Default Description
formConfigName String DeveloperName of FormConfig__mdt
formConfigJson String Inline JSON (overrides metadata)
targetObjectApiName String Salesforce object for record creation
recordData Object {} Pre-populate with existing values
submitLabel String "Submit" Submit button text
cancelLabel String "Cancel" Cancel button text
successMessage String "Form submitted successfully!" Toast / success state message
showResetButton Boolean false Show Reset button
showCancelButton Boolean false Show Cancel button

Public @api Methods

Method Description
submit() Programmatically submit the form
reset() Reset all fields to defaults
getFormValues() Returns {fieldName: value} map
setFieldValue(name, val) Set a single field value

Events Fired

Event event.detail
fieldchange { fieldName, value, allValues }
formsubmit { formData, success, error? }
formreset
formcancel

Deployment

# Deploy everything
sf project deploy start --source-dir force-app

# Run Apex tests
sf apex run test --class-names DynamicFormBuilderControllerTest --result-format human

Salesforce Best Practices Applied

  • with sharing on Apex — respects record-level security.
  • @AuraEnabled(cacheable=true) on read-only method — enables client-side caching.
  • Schema API for dynamic DML — avoids hardcoded field maps, respects FLS (isCreateable()).
  • lwc:if / lwc:elseif — uses modern conditional rendering (no deprecated if:true).
  • @api + @track separation — public API vs internal reactive state.
  • Child reportValidity() — delegates validation to native lightning components, no custom regex duplication.
  • composed: true events — allows events to cross shadow DOM boundaries.
  • isExposed: false on child — prevents accidental direct page placement.
  • Target configs in meta XML — correct App Builder property panel per surface.

About

Reusable Salesforce LWC for building dynamic forms without hardcoding field layouts. It supports configurable fields, sections, validation, conditional visibility, multiple input types, and responsive layouts. Designed to simplify data collection and create flexible, reusable forms for Salesforce record pages and custom business processes.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages