Empower growth and innovation with the latest Front-end Dev insights

In 2026, the API returns 0 for inventory but the page shows “--”: is the frontend treating 0 as no value?

Oct 20, 2026 Read: 14

When the API returns 0 but the page shows “--”, in most cases the API did not fail to return data, and the field name is not misspelled. Instead, the frontend used truthy checks such as || or if (!x) at the value-access step, treating 0, empty string, and false together as “no value”. ?? only applies to null and undefined; it preserves 0 but does not catch NaN, and it cannot be mixed with || or logical AND without parentheses. Under common 2026 delivery habits, “no value” and “zero value” need to be handled at two layers; one || cannot cover everything.

Where 0 usually gets swallowed: common code patterns

During integration, the field in the API JSON is indeed 0, it is visible in the Network panel, but the page shows a dash. Looking into the code, the problem is usually on the value-access line. JavaScript falsy values include 0, -0, 0n, empty string, false, null, undefined, and NaN; a truthy fallback sweeps all of them together, while in business terms only null and undefined usually need a fallback.

  • Logical OR fallback: count || '--'; inventory 0 becomes a dash, so you cannot tell zero stock from no data.
  • Negation check: if (!amount) return '--'; when the amount is 0, the whole block displays as empty.
  • Ternary shorthand: value ? value : '-'; the effect is the same as ||, and 0 is swallowed.
  • Destructuring default: const { count = '--' } = data; the default only applies to undefined, and it cannot catch null.

These patterns share one trait: the check condition is broader than the business semantics. The acceptable standard is: only values explicitly defined by the business as “none” should get a fallback; all other values should be displayed as-is. When troubleshooting, first run three data sets—0, empty string, and null—through the list, detail, and export views.

Empty-value checks have four layers: define semantics before writing code

0 being swallowed is rarely a single-point mistake. Rather, the data, value-access, display, and fallback layers are all making lenient judgments, and in the end no one can say which layer lost the value. Once layered, each layer is responsible for one thing, and when a problem occurs you can narrow it down layer by layer.

  1. Data layer: confirm field semantics with the backend—null means no data, 0 means data exists and is zero, and the two cannot share one fallback; also lock down whether the field type is number or string.
  2. Value-access layer: use ?? and ?. only on fields that may be missing; optional chaining should be used only where a chained object may break, not along the entire chain, otherwise genuine missing data will be silently swallowed.
  3. Display layer: route everything through a formatting function, map “no value” to a dash, render 0 as 0, and handle precision and thousands separators separately for amount fields.
  4. Fallback layer: add assertions or reporting for fields that truly should not be undefined, so silent empty values surface in logs.

The value-access layer is about “not throwing errors”; the display layer is about “displaying correctly”. Mixing these two concerns in one expression makes rework more likely. A common approach is for the value-access layer only to use ?? and ?. to keep the chain working, while the display layer decides whether to show a placeholder.

Converging the display layer: one formatting function for list, detail, and export

Pulling empty-value rules into one formatting function is less work than writing a fallback in every component. The acceptable standard is: the same field displays consistently in list, detail, and export; 0, empty string, and null each have a defined display; quantity and amount rules are separate. Under common 2026 practices, this function lives in a utils directory with test cases, and one change takes effect site-wide.

There is an order trap here: Number('') and Number(null) both return 0. If you convert to number before checking for empty, empty string and null become 0 first and are then displayed as “zero stock”. The correct order is to check null and undefined first, then use Number.isFinite to check for a valid number, and finally format the numeric value.

  • Quantity fields: first check v == null, then check Number.isFinite(Number(v)); only if both fail show a dash.
  • Amount fields: convert to number first, then process by the smallest unit to avoid floating-point errors, and add thousands separators uniformly at the display stage.
  • Text fields: define empty string separately according to the business; in most cases display it as-is or show a placeholder hint, and do not conflate it with null.

How to choose between || and ??: do not mechanically replace them site-wide

The two are not a new-versus-old replacement; their semantics are different: || takes the first truthy value, while ?? takes the first defined value. Choose the wrong semantics and 0 is lost. Mechanically replacing || with ?? across the entire site is not recommended; that is another kind of rework.

  • Fallback scope: || catches all falsy values, including 0, empty string, and false; ?? catches only null and undefined.
  • Whether 0 is preserved: || does not preserve it; ?? does.
  • Whether NaN is caught: neither catches it; NaN is not null/undefined and needs a separate Number.isFinite check.
  • Syntax restriction: ?? cannot be mixed directly with || or logical AND; parentheses are required, otherwise parsing fails.
  • Change risk: a full replacement carries relatively high risk, and logic that originally relied on empty-string fallbacks can break, especially form backfilling and search-condition assembly.

Experience range comparison of three convergence approaches

  • Point fix on a single page: change only the current page's fallback; typical range is 0.5–2 hours per page. Suitable for temporary campaign pages, but list, detail, and export views easily end up with separate implementations.
  • Replace || with ?? site-wide: large change surface; regression cost typically 1–3 person-days, and it can break old logic that depends on empty-string fallbacks.
  • Empty-value dictionary + unified formatting function: initial mapping typically takes 0.5–1 person-day; the more pages there are, the more worthwhile it becomes, and new pages reuse the same standard.

The preferred trade-off is: new code should use ?? by default to express “default”, and use || only when you genuinely want to catch empty string or false, with a comment explaining the intent. The next person who takes over does not have to guess whether the line was intentional or a slip.

Delivery case: the rework when 0 was treated as out of stock

A common type of admin project rework goes like this: Constraints: the backend field documentation only said “inventory, number” and did not explain the business semantics of 0 and null; the frontend followed past habits and used || to fall back to a dash, and there was no zero-stock sample during integration. Approach: the list page went live first, then detail and export each got their own fallback implementation, leaving the three views inconsistent. Result: after launch, operations saw products with inventory 0 displayed as a dash, misjudged them as out of stock, and manually delisted several products that were on sale; later the display layer was consolidated into a unified formatting function, and list, detail, and export were reworked using the four-layer method, with the rework falling within the experience range of 1–3 person-days.

Applicable and non-applicable boundaries

Scenarios suitable for this layered approach: admin systems, order/inventory/amount displays, the same data standard shared by H5 and mini-programs, form backfilling, and report pages exported externally. In these scenarios, a field value of 0 usually has business meaning, and losing it once can cause misjudgment.

Situations that do not need this level of rigor: purely static display pages, or text fields that cannot be 0 in the first place; writing a separate formatting function for them only expands the maintenance surface. For temporary pages for one-off campaigns, if there are no amount or quantity fields, the simplest fallback is enough.

A boundary judgment that can be quoted independently: only when 0 for a field truly has business meaning is it worth designing a separate empty-value standard for it; otherwise a unified fallback saves more maintenance cost.


Frequently asked questions

What exactly does 0 ?? '--' return?

It returns 0. ?? takes the right side only when the left side is null or undefined; 0 counts as having a value and is preserved as-is.

Why does writing ?? together with || cause an immediate error?

The specification requires parentheses when they are mixed; otherwise it is treated as a syntax error at parse time. A common pattern is to wrap the ?? part in parentheses to make the precedence clear.

Can a destructuring default catch null?

No. A destructuring default, like a function default parameter, only applies when the value is undefined; null is assigned as-is and needs to be handled once more with ?? at the value-access layer.

?? cannot catch NaN; how should it be checked?

Check it separately with Number.isFinite or Number.isNaN. NaN is not null/undefined, so ?? lets it through as a valid value, and NaN appears on the page.

Why does the page look normal when the API returns the string '0'?

Because the non-empty string '0' is truthy, so || does not replace it. The same field behaves differently as a number and as a string; unify the type first, then discuss fallbacks.

Have a similar project in mind?
Contact us for a one-to-one project reference proposal
Obtain Proposal
Interested in this topic?
10-year tech team — reference proposal within 24 hours
Obtain Proposal
Are you ready?
Then reach out to us!
+86-13370032918
Discover more services, feel free to contact us anytime.
Please fill in your requirements
What services would you like us to provide for you?
Your Budget
ct.
Our WeChat
Professional technical solutions
Phone
+86-13370032918 (Manager Jin)
The phone is busy or unavailable; feel free to add me on WeChat.
E-mail
349077570@qq.com
Submitted successfully
Thank you for your trust. We will contact you soon!
Recommended projects for you