Javascript dynamic object keys explained with examples

Javascript dynamic object keys explained with examples

Learn how to use dynamic object keys in JavaScript. Explore ES6 bracket notation, computed keys, and ternary operators to write cleaner, more flexible JS code.

Most JavaScript objects start with keys written directly into the code. That approach works until you need a key that comes from a variable, a function result, or a field name you do not know until the code runs. This is where dynamic object keys come in, and you will reach for this pattern more often than you might expect.

This guide covers what dynamic object keys are, how they evolved with ES6, where to use them in real projects, TypeScript considerations, and the edge cases that catch developers off guard.

What are static and dynamic object keys?

A static key is one you write as a literal string when defining an object.

const user = {
  name: 'Jane',
  age: 24
};

A dynamic key is one whose name is determined at runtime. You use a variable or expression inside square brackets, and JavaScript evaluates it to produce the final key name.

const field = 'email';

const user = {
  name: 'Jane',
  [field]: 'jane@example.com'
};

// Output: { name: 'Jane', email: 'jane@example.com' }

The [field] syntax inside the object literal is a computed property name, introduced in ES2015. JavaScript evaluates the expression once at object creation and uses the result as the key.

How dynamic keys worked before ES6

Before computed property names, adding a key from a variable required a two-step process. You created the object first, then assigned the dynamic key separately using bracket notation.

const field = 'location';

const user = { name: 'Jane', age: 24 };
user[field] = 'Nicaragua';

// Output: { name: 'Jane', age: 24, location: 'Nicaragua' }

This works, but it splits the object definition across multiple statements. As objects grow or the number of dynamic keys increases, the code becomes harder to follow.

The ES6 computed property syntax

ES2015 solved this by letting you define dynamic keys directly inside the object literal using square brackets.

const field = 'location';

const user = {
  name: 'Jane',
  age: 24,
  [field]: 'Nicaragua'
};

The full object lives in one place. The key is still evaluated at runtime, but the structure reads as a single coherent definition. Using { [key]: value } inside the literal is now the standard approach for ES6+ projects, keeping the object self-contained and easier to read.

Using expressions and conditions as keys

The brackets accept any valid JavaScript expression, including string concatenation, ternary operators, function calls, and template literals.

const category = 'lottery';
const didWin = true;

const result = {
  name: 'Doug',
  [category + (didWin ? 'Winner' : 'Loser')]: 'Prize data here'
};

// Output: { name: 'Doug', lotteryWinner: 'Prize data here' }
const prefix = 'user';
const role = 'admin';

const config = {
  [`${prefix}_${role}`]: true
};

// Output: { user_admin: true }

The expression is evaluated once and the result becomes the key string. This makes it easy to build objects that follow a naming pattern derived from existing data.

Practical use cases

Controlled form inputs in React

This is the most common real-world use case. A single onChange handler updates any form field by name without a separate handler per input.

function handleChange(e) {
  const { name, value } = e.target;
  setFormData(prev => ({ ...prev, [name]: value }));
}

Every input's name attribute becomes the key. One handler covers the entire form regardless of how many fields it has.

Building a lookup table from an array

When you need to index a list by a unique property, dynamic keys let you build the map cleanly inside a reduce.

const users = [
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' }
];

const userMap = users.reduce((acc, user) => ({
  ...acc,
  [user.id]: user
}), {});

// Output: { '1': { id: 1, name: 'Alice' }, '2': { id: 2, name: 'Bob' } }

Mapping API response field names to a new shape

When an API returns the field name as a string and you need to stamp it onto a new object without knowing the key name in advance.

const apiField = 'createdAt';
const apiValue = '2026-01-15';

const record = {
  id: 101,
  [apiField]: apiValue
};

Updating a named field in state

Any function that receives a field name and a value can return an immutable update using a dynamic key.

function updateField(state, fieldName, value) {
  return { ...state, [fieldName]: value };
}

TypeScript and dynamic object keys

TypeScript requires you to declare what kinds of keys are valid on an object type. Using a dynamic key on a typed object without an index signature causes a type error. The Record utility type is the cleanest fix for most cases.

const formData: Record<string, string> = {};
const field = 'email';
formData[field] = 'jane@example.com';

When only specific keys are valid, mapped types and keyof give you type safety on both the key and the value. TypeScript infers the value type from the key you pass and rejects mismatches at compile time.

type User = { name: string; email: string; age: number };

function updateUser<K extends keyof User>(user: User, key: K, value: User[K]): User {
  return { ...user, [key]: value };
}

Edge cases worth knowing

  • Key coercion is silent. The expression inside brackets is coerced to a string unless it is a Symbol. If the expression evaluates to undefined, the key becomes the literal string "undefined" with no warning or error.
const field = undefined;
const obj = { [field]: 'value' };
// Output: { undefined: 'value' }

Validate key values before using them as keys, especially when they come from API responses or user input.

  • Prototype pollution. If key names come from external input, an attacker can pass values like __proto__ or constructor to modify the object prototype. Sanitize any keys that originate outside your codebase.

  • Symbol keys behave differently. Symbol keys are excluded from JSON.stringify(), for...in, and Object.keys(). They do not appear in standard enumeration. This is intentional when you want non-enumerable metadata, but it causes confusion if you expect Symbol-keyed properties to serialize.

  • Object.fromEntries() is the complement pattern. When building an entire object from a list of key-value pairs, Object.fromEntries() is cleaner than a reduce with dynamic keys.

const entries = [['name', 'Jane'], ['age', 24]];
const user = Object.fromEntries(entries);
// Output: { name: 'Jane', age: 24 }

FAQs

1, Can I use a function call as an object key?

Yes. Any expression that evaluates to a string or Symbol is valid. [getFieldName()] works as long as getFieldName() returns a string.

2, What happens if the expression evaluates to null?

The key becomes the string "null". Same coercion behavior as undefined. Neither throws an error, which is exactly what makes these bugs easy to miss in production.

3, Is this the same as bracket notation for reading values?

The syntax looks similar but the context is different. obj[key] is access, used to read or assign a value on an existing object. { [key]: value } is a computed property name used to define a key during object creation.

4, When should I use a Map instead of a plain object?

Use a Map when keys need to be non-strings such as objects or numbers, when insertion order must be guaranteed explicitly, or when you are adding and deleting entries frequently at scale. For plain data objects that need JSON serialization, dynamic object keys are the right tool.

5, Does this work in all modern environments?

Computed property names have been part of ES2015 for over a decade and work in every modern browser and Node.js version. There are no compatibility concerns.

About author

I love solving problems, which has led me from core engineering to developer advocacy and product management. This is me sharing everything I know.

Book A Call!

Reach Your Technical Audience And Drive Product Adoption.

We are engineers, developer advocates, and marketers passionate about creating lasting value for SaaS teams. Partner with us to create the human-written developer marketing, SEO, demand-gen, and documentation content.

Get started

*35% less cost, risk-free, no lock-in.

Logo 1
Logo 2
Logo 3
Logo 4
Logo 5
Logo 6
Logo 7
Logo 8
Logo 9
Logo 10
Logo 11
Logo 12
Logo 13
Logo 14
Logo 15
Logo 16
Logo 17
Logo 18