Skip to main content

Building Dynamic Field Sets in Salesforce LWC: UI API & Schema Guide

In plain words: A Dynamic Field Set Component in Lightning Web Components (LWC) automatically renders form fields based on a Salesforce Field Set configuration rather than hardcoded HTML templates. By pairing the standard getObjectInfo wire adapter with <lightning-record-edit-form>, developers can build configurable forms that adapt instantly whenever administrators add or remove fields in Setup.

Hardcoding input fields inside custom components creates rigid user interfaces that break whenever business requirements change. Salesforce Field Sets provide a declarative way to group fields for specific layouts or processes. Building a dynamic LWC component that consumes object metadata allows you to render fields dynamically, reducing code duplication and giving administrators control over form layouts.

1. Architectural Overview: UI API & Field Sets

Retrieving and rendering field sets dynamically in LWC relies on native platform metadata services:

  • getObjectInfo Wire Adapter: Part of lightning/uiObjectInfoApi, this wire service fetches object metadata—including available field sets, field types, and descriptions—directly in JavaScript without writing custom Apex.
  • <lightning-record-edit-form>: Automatically handles data validation, error messages, and database commits for dynamically iterated field paths.
  • Template Iteration (for:each): Loops through the field set's fields array to render individual <lightning-input-field> elements dynamically.
360 Dynamic Field Set Architecture Card:
  • Metadata Source: lightning/uiObjectInfoApi (getObjectInfo).
  • Form Wrapper: <lightning-record-edit-form>.
  • Field Renderer: <lightning-input-field>.
  • Security Advantage: Respects organization-wide defaults, sharing rules, and Field-Level Security (FLS) automatically.

2. Step-by-Step Implementation

Step 1: Component Markup (dynamicFieldSet.html)
<template>
    <lightning-card title="Dynamic Field Set Form" icon-name="standard:record">
        <div class="slds-p-around_medium">
            <lightning-record-edit-form object-api-name={objectApiName} record-id={recordId}>
                <lightning-messages></lightning-messages>
                
                <div class="slds-grid slds-wrap slds-gutters">
                    <template for:each={fieldSetFields} for:item="field">
                        <div key={field.fieldPath} class="slds-col slds-size_1-of-1 slds-medium-size_1-of-2 slds-m-bottom_small">
                            <lightning-input-field 
                                field-name={field.fieldPath} 
                                required={field.required}>
                            </lightning-input-field>
                        </div>
                    </template>
                </div>

                <div class="slds-m-top_medium">
                    <lightning-button variant="brand" type="submit" label="Save Changes"></lightning-button>
                </div>
            </lightning-record-edit-form>
        </div>
    </lightning-card>
</template>
Step 2: JavaScript Controller (dynamicFieldSet.js)
import { LightningElement, api, wire } from 'lwc';
import { getObjectInfo } from 'lightning/uiObjectInfoApi';

export default class DynamicFieldSet extends LightningElement {
    @api recordId;
    @api objectApiName;
    @api fieldSetName; // Name of the field set configured in Setup

    objectInfoData;

    // Fetch object metadata including field sets using wire adapter
    @wire(getObjectInfo, { objectApiName: '$objectApiName' })
    wiredObjectInfo({ error, data }) {
        if (data) {
            this.objectInfoData = data;
        } else if (error) {
            console.error('Error loading object metadata:', error);
        }
    }

    // Getter to parse and return fields belonging to the specified field set
    get fieldSetFields() {
        if (!this.objectInfoData || !this.fieldSetName) {
            return [];
        }

        const fieldSets = this.objectInfoData.fieldSets;
        if (fieldSets && fieldSets[this.fieldSetName]) {
            return fieldSets[this.fieldSetName].fields;
        }

        return [];
    }
}
Step 3: Component Configuration (dynamicFieldSet.js-meta.xml)
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>60.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__RecordPage</target>
    </targets>
    <targetConfigs>
        <targetConfig targets="lightning__RecordPage">
            <property name="fieldSetName" type="String" label="Field Set API Name" description="Enter the API name of the target field set" />
        <ะฒะธั‰ะฐ/>
    </targetConfigs>
</LightningComponentBundle>

3. Common Traps & Best Practices

Metadata Trap: Missing Field Set API Names or Unassigned Objects
If the specified fieldSetName string does not match the exact DeveloperName of a field set configured on that object in Setup, objectInfoData.fieldSets[this.fieldSetName] will return undefined, rendering an empty form. Always verify field set API names in Object Manager before deploying.
Core Rule: Use lightning/uiObjectInfoApi wire adapters to retrieve field sets dynamically without Apex, expose configurable properties in meta.xml, and handle null object states gracefully.
  • Leverage Target Config Properties: Exposing fieldSetName as a configurable property in your js-meta.xml allows administrators to reuse the exact same LWC component across multiple objects and record pages with different field sets.
  • Ensure FLS Compliance: Because fields are rendered dynamically via <lightning-input-field>, Salesforce automatically hides fields the active user does not have permission to view or edit.

Summary

Building dynamic field set components in Salesforce Lightning Web Components bridges the gap between declarative configuration and custom UI development. By utilizing getObjectInfo wire adapters and <lightning-record-edit-form>, developers can deliver flexible, maintainable forms that adapt instantly to administrative changes.