DEV Community

Cover image for Understanding Nested Objects in JavaScript: A Complete Guide with Examples
WISDOMUDO
WISDOMUDO

Posted on

Understanding Nested Objects in JavaScript: A Complete Guide with Examples

Nested objects let you model real-world, hierarchical data, users with profiles, orders with items, and settings with sub-settings using plain JavaScript. By mastering nested objects, you’ll write code that is cleaner, safer, and simpler to manage. In this tutorial, we’ll walk through their use with straightforward patterns and hands-on examples. First, we’ll highlight what you can expect to learn, then move from creating objects to accessing, updating, iterating, and cloning them; each part connected to the next for a smooth learning journey.

What you’ll learn

By the end, you’ll be able to:

  • Create and visualize nested objects.
  • Access deep properties with dot/bracket notation and optional chaining.
  • Update and add nested values safely (mutably and immutably).
  • Iterate and search through nested structures.
  • Destructure nested values for cleaner code.
  • Clone and merge deeply without common pitfalls.

Now that you know the goals, let’s start by quickly defining what a nested object looks like.

What is a nested object?

A nested object is nothing more than an object that includes additional objects as values.

const user = {
id: 42,
profile: {
name: { first: "Wisdom", last: "Udo" },
contact: { email: "wisdom@example.com", phone: null }
},
preferences: { theme: "dark", notifications: { email: true, sms: false } }
};
Enter fullscreen mode Exit fullscreen mode

This setup reflects real-world data and organizes related information together. With the concept clear, let’s see how to create and grow these structures.

Next: creating and visualizing nested objects.

Creating and visualizing nested objects

Nested objects can be initialized in one go or assembled step by step.

// One-shot
const settings = {
ui: { theme: "light", density: "comfortable" },
auth: { token: "abc1234", expiresIn: 3600 }
};
// Step-by-step (useful when fields are conditional)
const config = {};
config.api = {};
config.api.endpoints = { users: "/api/users", posts: "/api/posts" };
Enter fullscreen mode Exit fullscreen mode

Tip: Log deep pieces during development to “see” the structure.

console.log(settings.ui.theme); // "light"
console.dir(config, { depth: null });
Enter fullscreen mode Exit fullscreen mode

Now that we can create and inspect nested objects, we need to read values from them safely.

Next: accessing deep properties.

Accessing nested properties (safely)

Use dot notation when accessing known keys, but switch to bracket notation for variable keys or those with spaces.

const lastName = user.profile.name.last;     // dot
const key = "email";
const email = user.profile.contact[key];     // bracket
Enter fullscreen mode Exit fullscreen mode

To avoid the TypeError: Cannot read properties of undefined, apply optional chaining (?.) and nullish coalescing (??).

const phone = user.profile?.contact?.phone ?? "No phone on file";
Enter fullscreen mode Exit fullscreen mode

Arrays inside objects? Combine with indexes:

const order = { items: [{ sku: "A2", qty: 2 }] };
const firstSku = order.items?.[0]?.sku; // "A2"
Enter fullscreen mode Exit fullscreen mode

Now that we’ve seen how to read nested values, the next step is learning how to write and update them.

Next: updating and adding nested values.

Updating and adding nested values

Direct mutation is the simplest:

user.preferences.notifications.sms = true;
user.profile.contact.phone = "+234-000-0000";
Enter fullscreen mode Exit fullscreen mode

But mutation can surprise other parts of your app if they share the same reference. For safer patterns, especially in React/Redux, use immutable updates with object spread:

const updatedUser = {
...user,
preferences: {
...user.preferences,
notifications: {
...user.preferences.notifications,
sms: true
}
}
};
Enter fullscreen mode Exit fullscreen mode

Adding new branches is the same pattern:

const withLocale = {
...user,
profile: {
...user.profile,
locale: { lang: "en", region: "GB" }
}
};
Enter fullscreen mode Exit fullscreen mode

Now that we can read and write data, let’s walk through it programmatically.

Next: iterating and searching nested objects.

Iterating and searching nested structures

Shallow iteration:

Object.keys(user.preferences).forEach(k => {
console.log(k, user.preferences[k]);
});
Enter fullscreen mode Exit fullscreen mode

For deep traversal, a small recursive helper is handy:

function getByPath(obj, path) {
return path.split(".").reduce((acc, key) => acc?.[key], obj);
}
getByPath(user, "profile.name.first"); // "Wisdom"
Enter fullscreen mode Exit fullscreen mode

Or to visit every key/value:

function walk(obj, visit, basePath = "") {
for (const [k, v] of Object.entries(obj)) {
const path = basePath ? ${basePath}.${k} : k;
visit(path, v);
if (v && typeof v === "object" && !Array.isArray(v)) {
walk(v, visit, path);
}
}
}
walk(user, (path, value) => console.log(path, value));
Enter fullscreen mode Exit fullscreen mode

Once traversal is clear, it becomes possible to efficiently select or reorganize data.

Next: using destructuring to extract nested values.

Destructuring nested objects

Destructuring allows you to extract fields in a single statement, while also supporting defaults and renaming.

const {
profile: {
name: { first: firstName, last: lastName },
contact: { email, phone = "N/A" }
},
preferences: { theme = "light" }
} = user;
console.log(firstName, lastName, email, phone, theme);
Enter fullscreen mode Exit fullscreen mode

This keeps usage clean where you need specific fields. After extracting, you may need to clone or merge objects without breaking references.

Next: cloning and merging safely.

Shallow copies (spread or Object.assign) only copy the top level:

const shallow = { ...user }; // profile/ preferences are still shared references
Enter fullscreen mode Exit fullscreen mode

For a deep clone, prefer structuredClone in modern environments:

const deep = structuredClone(user);
deep.profile.name.first = "Joy";
console.log(user.profile.name.first); // still "Wisdom"
Enter fullscreen mode Exit fullscreen mode

Developers often use JSON.parse(JSON.stringify(obj)) as a quick fallback, though it drops functions, Date, Map, Set, and undefined. For a more reliable solution, utilities like Lodash’s _.cloneDeep are preferred.

For deep merging (combine nested branches), either use a library or a tiny recursive merge:

function deepMerge(target, source) {
const out = { ...target };
for (const [k, v] of Object.entries(source)) {
if (v && typeof v === "object" && !Array.isArray(v)) {
out[k] = deepMerge(target[k] ?? {}, v);
} else {
out[k] = v;
}
}
return out;
}
const merged = deepMerge(user, { preferences: { notifications: { push: true } } });
Enter fullscreen mode Exit fullscreen mode

With cloning and merging in your toolkit, a few final pitfalls will help you avoid bugs.

Next: Common Pitfalls and Quick Fixes.

Common pitfalls & quick fixes

  • Undefined paths: Use optional chaining (a?.b?.c) and defaults (??).
  • Shallow copy surprises: Use structuredClone or a deep-clone utility when you truly need independence.
  • Dynamic keys: Always switch to bracket notation (obj[key]), not dot (obj.key).
  • Arrays vs. objects: Check types before recursing; arrays need index handling.
  • To maintain a consistent state in UI applications, prefer immutable updates instead of frequent mutations.

With concepts and safeguards in place, let’s wrap up.

Conclusion

Nested objects are the backbone of modeling complex data in JavaScript. You’ve seen how to create nested objects, safely access their values, update them immutably, iterate and search with helper functions, use destructuring for cleaner code, and clone or merge without errors. By applying these patterns, your code will stay strong, easy to read, and simple to maintain.

A practical way to reinforce these ideas is to fetch a JSON response, identify its nested paths, build a function like getByPath, and use immutable spread to handle deep updates. This exercise will solidify your learning.

You can reach out to me via LinkedIn

Top comments (0)