The Percent Field Display Gotcha in Lightning Web Components
One of the most common and confusing display bugs in LWC development involves percent fields showing values 100 times larger than expected. A field that should display as 2% instead shows as 200%. This article explains the root cause and the fix.
The Symptom
You query a record with a percent field and display it using lightning-formatted-number:
<lightning-formatted-number
value={record.Markup_Percent__c}
style="percent"
minimum-fraction-digits="2"
></lightning-formatted-number>
The field in Salesforce shows 2.00%, but your component renders 200.00%.
The Root Cause
There is a mismatch between how Salesforce stores percent values and how the lightning-formatted-number component with style="percent" expects them.
Salesforce storage format: Percent fields are stored as whole numbers. A value of 2% is stored as 2 in the database and returned as 2 from Apex queries and wire adapters.
lightning-formatted-number expectation: When style="percent" is set, the component follows the JavaScript Intl.NumberFormat convention, which expects a decimal fraction. To display 2%, it expects the input value 0.02. It then multiplies by 100 internally for display.
The result: passing 2 (the Salesforce storage value) to a percent-styled formatter produces 2 * 100 = 200%.
The Fix
Divide the Salesforce value by 100 before passing it to the formatter.
Option 1: Getter in the JavaScript Controller
get markupPercentDecimal() {
if (this.record?.Markup_Percent__c == null) return null;
return this.record.Markup_Percent__c / 100;
}
<lightning-formatted-number
value={markupPercentDecimal}
style="percent"
minimum-fraction-digits="2"
></lightning-formatted-number>
Option 2: Transform During Data Processing
When you process records into a display-ready format (common in table components), apply the conversion at that stage:
get displayItems() {
return this.items.map((item) => ({
...item,
markupForDisplay: item.Markup_Percent__c != null
? item.Markup_Percent__c / 100
: null,
}));
}
When NOT to Convert
If you are displaying the value as a plain number (without style="percent"), do not divide by 100. The raw value is correct for numeric display:
<!-- This correctly shows "2" -->
<lightning-formatted-number
value={record.Markup_Percent__c}
></lightning-formatted-number>
<!-- Append the percent sign manually if needed -->
<span>{record.Markup_Percent__c}%</span>
The conversion is only necessary when using style="percent", which triggers the Intl.NumberFormat percent formatting behavior.
Where to Apply the Conversion
Apply the division in the JavaScript controller, not in the template. LWC templates do not support arithmetic expressions, so you cannot write value={record.Markup_Percent__c / 100} inline. This is by design -- LWC encourages moving logic into the controller.
For components that display many percent fields, a utility function keeps things consistent:
// percentUtils.js (shared utility module)
export function toDisplayPercent(value) {
return value != null ? value / 100 : null;
}
// In your component
import { toDisplayPercent } from "c/percentUtils";
get markupForDisplay() {
return toDisplayPercent(this.record.Markup_Percent__c);
}
Testing the Fix
Verify with boundary values:
| Salesforce Value | After Division | Displayed As |
|---|---|---|
0 | 0 | 0.00% |
2 | 0.02 | 2.00% |
100 | 1 | 100.00% |
0.5 | 0.005 | 0.50% |
null | null | (empty) |
This gotcha affects every Salesforce percent field displayed with lightning-formatted-number using percent style. Once you know the pattern, it becomes a quick check during code review: if you see style="percent", confirm the value is being divided by 100.