Why Your UI Shows [object Object] and How to Fix It Fast

Seeing [object Object] instead of a title is a classic JavaScript issue. Learn exactly why it happens in React and other UIs, how to debug it quickly, and the patterns that prevent it for good.

ByTalosTalos
Energy
Why Your UI Shows [object Object] and How to Fix It Fast

The moment you see it

You push a build, reload the page, and there it is: a card that should list recent titles now reads [object Object]. Your first thought is that the data must be wrong. In reality, the browser is telling you something precise. Some code tried to turn a JavaScript object into a string without telling it how to present itself. The platform did the only thing it knows to do and produced a string token that literally says it is an object.

The root cause is straightforward. When JavaScript needs to render or concatenate a value into text, it asks the value to describe itself as a string. For plain objects, the default object toString returns the tag "[object Object]". That return value is useful for debugging types, but it is useless for your end users.

This guide explains why that happens, where it usually pops up, and how to fix it in minutes. It also shows how to design your data flow so the problem does not come back.

Why it happens

JavaScript performs implicit string conversion in many places:

  • Template literals and string concatenation
  • JSX text nodes in frameworks like React and Preact
  • DOM APIs that expect strings, such as textContent
  • Logging and error messages that interpolate variables

If the value you interpolate is an object, JavaScript calls toString() or valueOf() to get a text representation. For plain object literals, toString() yields "[object Object]". That is why the symptom appears in any UI stack that relies on JavaScript stringification.

The most common places it sneaks in

You will see this pattern show up in a handful of repeatable situations.

1) Template strings with raw objects

const article = { title: "Travel rules update" };
const msg = `Latest: ${article}`; // "Latest: [object Object]"

Fix by selecting the exact field you want.

const msg = `Latest: ${article.title}`;

2) JSX text nodes with unformatted props

function Card({ title }) {
  return <h3>{title}</h3>;
}

const title = { text: "Inflation cools in August" };
<Card title={title} /> // renders [object Object]

Fix by passing a string, not an object.

<Card title={title.text} />

3) Generic list renderers that assume strings

A reusable component might accept an array of items and render each item inside a <li>. If items is an array of objects and the component blindly does {item}, you will get [object Object] for each row.

Fix by providing a projection function or a renderItem prop that maps each object to a human readable string.

4) I18n and templating with unformatted values

Translation strings often include placeholders. If the code hands an entire object to a placeholder instead of a field, the templating library will stringify it.

Fix by passing primitives to the translation call, not composite objects.

5) Logging that hides the real issue

Sometimes [object Object] appears in logs or error toasts. The user facing toast is interpolating a whole error object rather than the message. You will see [object Object] on the screen and a vague bug report. Meanwhile the console has the real stack trace.

Fix by displaying error.message or a mapped message, and logging the full object to the console or your telemetry system.

A fast debugging checklist

Use this list when you encounter the symptom in production or during review. For a deeper walkthrough, see the rendering checklist guide.

  1. Reproduce the state
  • Navigate to the view that shows [object Object] and capture the data payloads from your network tab.
  1. Inspect the value types
  • In the component or function that renders the string, add a temporary log:
console.log('title type:', typeof title, title);
  • If it says object, look at the keys that are available.
  1. Render the one field you actually want
  • Most UIs want item.title, user.name, or error.message. Pick the right field and render that.
  1. Guard against missing data
  • If your field can be null or undefined, supply a default:
<h3>{item?.title ?? 'Untitled'}</h3>
  1. Avoid blanket JSON.stringify in the UI
  • JSON.stringify is handy for debugging but do not commit it as the final user facing text. It reveals raw structure and may expose sensitive fields.

Fixes that stick

Once you have patched the immediate bug, add a couple of structural guard rails.

Create tight component contracts

If a component is named Title, it should accept a string called text. Not a generic data prop. Not an entire article object. Constrain the API and TypeScript will help keep it honest.

type TitleProps = { text: string };

function Title({ text }: TitleProps) {
  return <h2>{text}</h2>;
}

// Good
<Title text={article.title} />

// Avoid
<Title text={article} />

Validate at the edges

When data enters the app, validate it once and transform it to the shape your views expect. Use a schema library to ensure that title is always a string.

import { z } from 'zod';

const Article = z.object({
  id: z.string(),
  title: z.string(),
  excerpt: z.string().optional()
});

const parsed = Article.parse(serverPayload);

Now your components can assume title is a string, which prevents accidental object interpolation. For more patterns, see data validation best practices.

Prefer formatters over ad hoc interpolation

Create small utilities to format domain objects instead of sprinkling ${object} across the codebase.

function formatUser(u: { first: string; last: string }) {
  return `${u.first} ${u.last}`;
}

function formatMoney(cents: number, currency = 'USD') {
  return new Intl.NumberFormat('en-US', { style: 'currency', currency }).format(cents / 100);
}

When you find yourself writing ${obj}, stop and ask which formatter should exist.

Add lint rules that catch implicit coercion

Enable rules that flag suspicious string concatenations. Examples:

  • Disallow + with non primitives in your codebase except in very specific utils
  • Prefer template literals that call clear formatters
  • Use TypeScript’s noImplicitAny and strict null checks so accidental object values are obvious

Unit tests for rendering

Snapshot tests sometimes normalize [object Object] into harmless looking output, which hides the bug. Prefer explicit rendering tests that assert the presence of meaningful text.

test('Card renders a title string', () => {
  render(<Card title={{ text: 'Hello' }} />);
  expect(screen.getByRole('heading', { level: 3 })).toHaveTextContent('Hello');
});

This test will fail until you pass title.text to the component.

Special cases that fool people

Not every object is a plain object, and not every object stringifies the same way.

Objects that implement toString

Some libraries provide rich objects that define their own toString method. They will not produce [object Object]. They might emit a custom formatted string. This can hide problems because the UI appears to work until a different object that lacks toString slips in.

Fix by standardizing the component contract. Do not rely on an object’s magic methods for user facing text.

toJSON changes stringification

JSON.stringify checks for a toJSON method. If present, it uses that result. This is helpful for logs, but it can mislead you into thinking an object is safe to interpolate everywhere. Treat JSON.stringify as a debugging tool, not a rendering strategy. For details on options and the replacer parameter, see the MDN JSON.stringify reference.

Arrays and Maps

Rendering arrays directly will coerce them by joining with commas, which can be acceptable if the array contains strings. Rendering Maps or Sets will produce [object Map] or [object Set].

Fix by mapping to strings explicitly.

<ul>
  {tags.map(tag => <li key={tag}>{tag}</li>)}
</ul>

Defensive defaults that mask bugs

A common quick fix is const text = String(value || ''). That silences errors but loses signal. If value is an object, you still get [object Object]. Instead, throw early or surface a clear developer facing message so you can correct the data flow.

A robust pattern for list UIs

Your incident started with a list of recent titles that failed to render. Here is a small, robust pattern for that exact scenario.

// 1) Define the contract
interface RecentItem { id: string; title: string }

interface RecentListProps {
  items: RecentItem[]
}

// 2) Render only strings in text nodes
function RecentList({ items }: RecentListProps) {
  return (
    <ul aria-label="Recent posts">
      {items.map(item => (
        <li key={item.id}>
          <a href={`/posts/${item.id}`}>{item.title || 'Untitled'}</a>
        </li>
      ))}
    </ul>
  );
}

// 3) Validate and transform at the boundary
function normalize(raw: unknown): RecentItem[] {
  // Replace with your schema library in production
  if (!Array.isArray(raw)) return [];
  return raw
    .filter(x => x && typeof x === 'object')
    .map((x: any) => ({
      id: String(x.id ?? ''),
      title: typeof x.title === 'string' ? x.title : ''
    }))
    .filter(x => x.id);
}

This approach ensures the rendering layer never receives composite objects for text nodes. The component is simple and cannot accidentally print [object Object]. For more UI patterns, see error message patterns.

Production checklists

Use these checklists to prevent regressions during code review.

Rendering checklist

  • Are all text nodes primitives or results of formatters
  • Are object props used only for components that expect objects
  • Are null and undefined handled with safe defaults

Data checklist

  • Is incoming server data validated and normalized before reaching the view
  • Are the field names and types stable and documented
  • Do we avoid over fetching objects when we only need a simple field

Observability checklist

  • Do user facing toasts display human friendly messages
  • Do logs include the raw object for debugging without exposing it to the UI
  • Do we track the count of occurrences of [object Object] in the DOM during QA

Rethinking error messages

If you ever consider showing raw objects to users, stop and design the message. Users need a clear action, not internal state. Convert system details into user centered copy and send the full object to your telemetry platform for diagnostics.

For example, prefer this:

  • Screen: "We could not load recent titles. Try again in a moment."
  • Log: Full error object with stack trace, request ID, and user context

This separation keeps the user experience clean while preserving the information you need to fix the root cause.

A final word on culture

Bugs like [object Object] are usually the symptom of a small contract mismatch, not a big system failure. Encourage a culture that celebrates removing accidental complexity. Ask for clear component props, typed boundaries, and formatting helpers. Make it boring to render text correctly. Once the team adopts these patterns, this entire class of bugs fades away.

Quick reference

  • What it is: [object Object] appears when an object is implicitly converted to a string
  • Quick fix: render the exact field you want
  • Make it durable: strict component contracts and schema validation
  • Preferred approach: formatters over ad hoc interpolation
  • Test it: assert meaningful text, not just snapshots

Other articles you might like

Summer 2025 made batteries the grid’s new peaker plants

Summer 2025 made batteries the grid’s new peaker plants

A scorching Summer 2025 flipped the peaker playbook. ERCOT surpassed CAISO in battery capacity, Texas storage set a 6.3 GW evening discharge record on July 11, CAISO credited storage for stronger reserves, and Puerto Rico’s home batteries performed like a paid virtual power plant.

Order 1920’s trench fight: courts, clocks, and cost splits

Order 1920’s trench fight: courts, clocks, and cost splits

Order 1920 left the page and entered the trenches in 2025. Rehearing clarifications, a new FERC chair, Fourth Circuit consolidation, and a demand shock from data centers now collide with looming compliance deadlines that will decide who actually builds long lines.

Palisades Comes Back: The New Playbook for Reactor Re‑life

Palisades Comes Back: The New Playbook for Reactor Re‑life

With a fresh DOE loan disbursement on September 16 and summer NRC approvals, Palisades now has a credible path to a Q4 2025 restart. Here is the playbook behind the financing, offtake and licensing, what it means for MISO, and how SMR co-location could multiply the impact.

AI islands collide with Texas’s new 765 kV power superhighway

AI islands collide with Texas’s new 765 kV power superhighway

AI data centers are racing into the Permian on islanded gas-plus-battery microgrids while Texas readies 765 kV lines to move bulk ERCOT power west. SB 6 rewrites who pays, how to connect, and when operators must curtail.

America’s LNG Reboot: DOE ends pause, Gulf projects sprint

America’s LNG Reboot: DOE ends pause, Gulf projects sprint

The Department of Energy restored LNG approvals in January 2025, clearing a backlog and jump-starting Gulf Coast projects. We break down who advanced, what volumes are realistic for 2026 to 2030, and which bottlenecks like pipelines, labor and methane rules could still slow the surge.

How 2025 locked in the West’s two-track day-ahead market

How 2025 locked in the West’s two-track day-ahead market

In 2025, CAISO’s EDAM and SPP’s Markets+ moved from debate to buildout. Here is who joins when, how the seam will work, and what changes by 2026 to 2028.

Who Connects First Now: PJM, MISO and CAISO’s Fast Tracks

Who Connects First Now: PJM, MISO and CAISO’s Fast Tracks

U.S. grid operators just opened express lanes for near‑term megawatts. We break down which PJM, MISO and CAISO projects can realistically hit 2027 to 2029 CODs, how PJM’s record capacity prices tilt technology choices, and the moves developers, LSEs and data centers should make now.

CCS’s 2025 permitting pivot rewires where carbon can go

CCS’s 2025 permitting pivot rewires where carbon can go

State primacy over Class VI wells and the first Gulf Coast permits are finally moving U.S. CCS from proposals to construction. Texas and Appalachia are speeding up while tougher pipeline standards may decide who reaches financing and rights of way first.

Tariffs reshape the U.S. solar map as 2025 trade cases widen

Tariffs reshape the U.S. solar map as 2025 trade cases widen

June 24 changed the U.S. solar market. Antidumping and countervailing duty orders on four Southeast Asian countries bent the price curve, and fresh probes into India, Laos, and Indonesia could force another pivot before year end. Here is what it means for sourcing, PPAs, and schedules.