Rather than writing complex, custom Apex schema classes or hardcoding options, Lightning Web Components (LWC) provide built-in wire adapters through lightning/uiObjectInfoApi. These adapters (getObjectInfo and getPicklistValues) handle the entire controlling-dependent mapping, caching, and record-type filtering automatically.
Prerequisites
- A Salesforce Developer Edition, Scratch Org, or active Sandbox.
- Salesforce CLI (
sf) installed and authenticated. - A standard or custom object with configured controlling and dependent picklist fields (e.g., standard
Account.Industrycontrolling a customSub_Industry__c).
Step 1: Create the Component Bundle
sf lightning generate component -n dependentPicklistDemo -d force-app/main/default/lwc --type lwc
Step 2: Build the HTML Template
In dependentPicklistDemo.html, declare two lightning-combobox elements. The dependent combobox remains disabled until a controlling parent selection is made:
<template>
<lightning-card title="Dynamic Dependent Picklist Demo" icon-name="standard:picklist_type">
<div class="slds-p-around_medium">
<div class="slds-grid slds-gutters slds-wrap">
<!-- Controlling (Parent) Picklist -->
<div class="slds-col slds-size_1-of-1 slds-medium-size_1-of-2 slds-m-bottom_small">
<lightning-combobox
name="controllingField"
label="Industry (Controlling)"
placeholder="Select Industry"
options={parentOptions}
value={selectedParentValue}
onchange={handleParentChange}>
</lightning-combobox>
</div>
<!-- Dependent (Child) Picklist -->
<div class="slds-col slds-size_1-of-1 slds-medium-size_1-of-2 slds-m-bottom_small">
<lightning-combobox
name="dependentField"
label="Sub-Category (Dependent)"
placeholder="Select Sub-Category"
options={childOptions}
value={selectedChildValue}
onchange={handleChildChange}
disabled={isChildDisabled}>
</lightning-combobox>
</div>
</div>
<!-- Selection Summary -->
<template lwc:if={selectedParentValue}>
<div class="slds-box slds-theme_shade slds-m-top_medium">
<p>Selected Controlling Value: <strong>{selectedParentValue}</strong></p>
<p>Selected Dependent Value: <strong>{selectedChildValue}</strong></p>
</div>
</template>
</div>
</lightning-card>
</template>
Step 3: Implement the UI Object Info JavaScript Wire Logic
In dependentPicklistDemo.js, wire the default record type using getObjectInfo, fetch both picklists with getPicklistValues, and parse the dependent mapping object returned by Salesforce:
import { LightningElement, wire } from 'lwc';
import { getObjectInfo, getPicklistValues } from 'lightning/uiObjectInfoApi';
import ACCOUNT_OBJECT from '@salesforce/schema/Account';
import INDUSTRY_FIELD from '@salesforce/schema/Account.Industry';
import SUB_CATEGORY_FIELD from '@salesforce/schema/Account.Type'; // Replace with your dependent field schema
export default class DependentPicklistDemo extends LightningElement {
parentOptions = [];
childOptions = [];
selectedParentValue = '';
selectedChildValue = '';
// Cached dependent metadata bundle
dependentPicklistData;
get isChildDisabled() {
return !this.selectedParentValue || this.childOptions.length === 0;
}
// Step 1: Retrieve Object Info to get Default RecordTypeId
@wire(getObjectInfo, { objectApiName: ACCOUNT_OBJECT })
objectInfo;
// Step 2: Fetch Controlling (Parent) Picklist Values
@wire(getPicklistValues, {
recordTypeId: '$objectInfo.data.defaultRecordTypeId',
fieldApiName: INDUSTRY_FIELD
})
wiredParentPicklist({ data, error }) {
if (data) {
this.parentOptions = data.values.map(item => ({
label: item.label,
value: item.value
}));
} else if (error) {
console.error('Error loading parent picklist values:', error);
}
}
// Step 3: Fetch Dependent (Child) Picklist Values & Controller Mapping
@wire(getPicklistValues, {
recordTypeId: '$objectInfo.data.defaultRecordTypeId',
fieldApiName: SUB_CATEGORY_FIELD
})
wiredChildPicklist({ data, error }) {
if (data) {
this.dependentPicklistData = data;
} else if (error) {
console.error('Error loading dependent picklist values:', error);
}
}
handleParentChange(event) {
this.selectedParentValue = event.target.value;
this.selectedChildValue = '';
this.filterDependentOptions();
}
handleChildChange(event) {
this.selectedChildValue = event.target.value;
}
filterDependentOptions() {
if (!this.dependentPicklistData || !this.selectedParentValue) {
this.childOptions = [];
return;
}
// Get key index of the selected controlling value
const controllerIndex = this.dependentPicklistData.controllerValues[this.selectedParentValue];
// Filter valid child values using validFor array indices
this.childOptions = this.dependentPicklistData.values
.filter(item => item.validFor.includes(controllerIndex))
.map(item => ({
label: item.label,
value: item.value
}));
}
}
getPicklistValues imperatively inside event handlers like handleParentChange. getPicklistValues is a standard wire adapter, not an imperative Apex method. Wire both picklist fields upfront, and filter the dependent choices on the client side using the validFor array and controllerValues map.
- validFor Mapping: Each dependent entry in
getPicklistValuesreturns an array of controller indices (validFor) designating which parent choices enable it. - controllerValues Map: Maps each parent string value to an integer index key (e.g.,
{"Banking": 0, "Consulting": 1}). - Record Type Awareness: Dynamically inherits record-type-specific field dependencies without extra SOQL or Apex code.
- Client Caching: All picklist metadata is provisioned and cached via Lightning Data Service (LDS) on initial page load.
Step 4: Expose and Deploy the Component
Update dependentPicklistDemo.js-meta.xml to expose the component in Lightning App Builder:
<?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__AppPage</target>
<target>lightning__RecordPage</target>
<target>lightning__HomePage</target>
</targets>
</LightningComponentBundle>
# Deploy to the default authenticated org
sf project deploy start
# Open target org in browser
sf org open
- Open Setup > Lightning App Builder.
- Drag
dependentPicklistDemoonto any Record or App page, then save and activate. - Choose a value in the Controlling picklist and verify that the Dependent picklist instantly filters and enables its matching sub-options.
lightning/uiObjectInfoApi wire adapters provides zero-Apex, record-type-aware dependent picklist filtering with built-in client-side caching.