dot-prop: reading and writing nested objects by path, and the paths you should not accept
Get, set, or delete a property from a nested object using a dot path
At a glance
- What is it?
- dot-prop gives you four operations on a nested object addressed by a dot path or by an array of segments: get, set, has and delete, plus helpers to escape, parse and stringify paths and to enumerate every key. The part worth reading twice is the security note, which refuses to sanitise attacker-controlled paths for you.
- Who is it for?
- Use dot-prop when your code has to read or write a nested object by a path that arrives as a string, because it handles the array syntax, the escaped dot and the numeric index normalisation for you. Do not use it on a path that came from an untrusted source without sanitising it yourself, because the library says so explicitly and lists only three components as blocked.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 98 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 25, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Four functions and one path grammar
The package description is a sentence: get, set, or delete a property from a nested object using a dot path. The API is four named exports. getProperty takes an object, a path and an optional default value, and returns the value if any. setProperty takes an object, a path and a value, and returns the object. hasProperty returns a boolean for whether the path exists. deleteProperty removes it and returns a boolean of whether the property existed beforehand. The path grammar is the interesting part. A dot separates nested keys, a backslash-dot escapes a literal dot in a key, and array indices can be written in bracket notation such as users[0].name or in dot notation such as users.0.name, with the two being equivalent and both creating arrays when you set a value. Numeric strings in dot notation are coerced to numbers automatically.
The getter and its three failure modes
Reading is where the design decisions show.
getProperty({foo: {bar: 'unicorn'}}, 'foo.bar');
//=> 'unicorn'
getProperty({foo: {bar: 'a'}}, 'foo.notDefined.deep');
//=> undefined
getProperty({foo: {bar: 'a'}}, 'foo.notDefined.deep', 'default value');
//=> 'default value'A path that runs off the end of the object returns undefined rather than throwing, and the optional third argument supplies a default in that case. That is the behaviour you want for configuration objects and user-supplied paths, and it is also the behaviour that hides typos, so a misspelled key in a path string will read as absent rather than as an error. The escaped-dot case is the other one worth knowing.
getProperty({foo: {'dot.dot': 'unicorn'}}, 'foo.dot\\.dot');
//=> 'unicorn'Without the escape, that key would be read as two nested lookups instead of one key containing a dot.
The setter creates the intermediate structure
Setting is where a dot path library can quietly decide your object's shape, and the examples show it creating arrays where you would expect an object.
const object = {foo: {bar: 'a'}};
setProperty(object, 'foo.bar', 'b');
console.log(object);
//=> {foo: {bar: 'b'}}
const foo = setProperty({}, 'foo.bar', 'c');
console.log(foo);
//=> {foo: {bar: 'c'}}
setProperty(object, 'foo.biz[0]', 'a');
console.log(object);
//=> {foo: {bar: 'b', baz: 'x', biz: ['a']}}So the setter builds the missing intermediate levels, and whether a level becomes an array or an object is decided by whether the segment looks like an index. That is convenient for the common case and a hazard when the path comes from outside, because a numeric-looking key in a user-supplied path will produce an array where your schema expected an object. The setter also returns the object, which is why the second example assigns the result rather than mutating a pre-existing variable.
Array paths skip the parsing cost
There is a second path format, and it exists for performance and interoperability. Passing an array of segments avoids the parse and stringify cycle entirely, which matters if you are reading paths in a hot loop or if the paths arrive from another library as arrays.
getProperty(object, ['users', 0, 'name']);
//=> 'Alice'
setProperty(object, ['users', 1, 'role'], 'moderator');
console.log(object.users[1].role);
//=> 'moderator'The four functions all accept arrays, numeric strings are normalised to numbers for simplicity, and the README notes the interoperability case explicitly, with an example where a path from another library is used to set a nested value. The helper functions make the two formats convertible, with parsePath turning a string into segments where numbers represent array indices and strings represent object keys, and stringifyPath doing the reverse. stringifyPath takes an options object with a preferDotForIndices flag, defaulting to false, which switches numeric indices to dot notation when they are not the first segment. So ['foo', 0, 'bar'] becomes 'foo[0].bar' by default and 'foo.0.bar' with the flag set.
escapePath is the boundary between a path and a key
escapePath escapes special characters in a path and is described as useful for sanitising user input. The idea is simple: an escaped path treats the whole string as one literal key, so a user-supplied value that happens to contain a dot addresses a key with that dot in it rather than two levels of nesting. That is the correct behaviour when the input is meant to be a key name rather than a path, and it is the reason the function exists separately from parsePath, whose output is the opposite, a list of segments.
The security note is short and blunt
The most important paragraph in the README is the important callout at the end of the path documentation. Sanitising untrusted path input is your responsibility, and escapePath is the tool offered for treating it as a literal key. Beyond the three components named as invalid, no protection against attacker-controlled paths is attempted, and a path can reach anything reachable from the object. Those three invalid components are __proto__, prototype and constructor, and a path containing them returns undefined. That is a narrow block list rather than a defence, and the README does not claim otherwise. The practical reading is that dot-prop is safe for paths your own code constructs or that come from a trusted configuration source, and is not a sanitiser for paths typed into a form. If you need that, treat the input as a key with escapePath or validate it against an allowlist of segments before you build a path from it.
deepKeys flattens an object for key-value APIs
deepKeys returns an array of every path in an object, with non-empty plain objects and arrays deeply recursed and not themselves included in the result. The stated use is flattening an object for an API that only accepts key-value pairs, or for a tagged template literal.
import {getProperty, deepKeys} from 'dot-prop';
const user = {
name: {
first: 'Richie',
last: 'Bendall',
},
activeTasks: [],
currentProject: null
};
for (const property of deepKeys(user)) {
console.log(`${property}: ${getProperty(user, property)}`);
}The output in the example shows the behaviour that matters: name.first and name.last are listed because they are non-empty plain objects, while activeTasks and currentProject appear as leaf values even though one is an empty array and one is null. Sparse arrays are supported, and the README links to an issue advising in general against sparse arrays.
A small ESM-only package with a typed surface
The package metadata says more about the constraints than the README does. It is type module with an exports map that has a types entry pointing at index.d.ts and a default entry at index.js, and there is no CommonJS entry, so a require of this package will not work without an interop layer. sideEffects is false, which lets a bundler drop the import entirely if nothing is used from it. The engines field requires Node 20 or newer. The single runtime dependency is type-fest at version 5, which is the type-only package, so the dependency list carries no runtime code. The published files list is just index.js and index.d.ts, so the package is two files. The test script runs xo for linting, ava for the tests and tsc for the type check, with a separate benchmark script and c8 for coverage, and the repository holds a benchmark.js at the root alongside index.js, index.d.ts, a type test file and test.js.
Editorial conclusion
Use dot-prop when your code has to read or write a nested object by a path that arrives as a string, because it handles the array syntax, the escaped dot and the numeric index normalisation for you. Do not use it on a path that came from an untrusted source without sanitising it yourself, because the library says so explicitly and lists only three components as blocked. Verify first that your paths are trusted, that any path built from user input goes through escapePath, that your Node version satisfies the engine requirement of 20 or newer, and that the package is still ESM only with no CommonJS entry, since the exports map has a single default and types entry.
Frequently asked questions
What does dot-prop do?
It gets, sets, checks and deletes a property on a nested object addressed by a dot path or by an array of path segments. It also provides escapePath, parsePath, stringifyPath and deepKeys as helpers.
Is dot-prop safe to use with paths from user input?
Not without your own sanitising. The README states that sanitising untrusted path input is your responsibility, that apart from __proto__, prototype and constructor no protection against attacker-controlled paths is attempted, and that a path can reach anything reachable from the object.
What is the difference between a string path and an array path?
Array paths avoid the parse and stringify cycle when you already have the segments, they work with all four functions, and numeric strings are normalised to numbers. The path can be written with bracket notation such as users[0].name or with dot notation such as users.0.name, and both are equivalent.
What does setProperty do when the path does not exist?
It creates the intermediate structure and returns the object. Whether a level becomes an array or an object depends on whether the segment looks like an index, so foo.biz[0] produces an array while foo.bar produces a nested object.
What are the requirements for installing dot-prop?
Node 20 or newer, and it is an ES module only package with a single types and default entry in its exports map and no CommonJS build. The only runtime dependency is type-fest, which is type-only, and the MIT licence is recorded in the package metadata.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/sindresorhus-dot-prop)Was this useful?