Library / SDK
pvorb/clone avatar
pvorb/clone

clone: deep copying in JavaScript, down to the circular reference

deeply clone arbitrary objects in javascript

790 stars136 forksJavaScriptMIT

At a glance

What is it?
clone is a small, dependency-free JavaScript library that copies objects, arrays, Maps, Sets, Dates, RegExps, Errors and Promises recursively, and it handles circular structures by default rather than by special case. It is a 2021-era favourite whose npm version has not moved since 2018, which tells you both how stable it is and how little maintenance it has seen.
Who is it for?
Adopt clone if you need a deep copy that preserves prototypes and handles circular references without writing a recursive copier yourself, particularly when you are copying configuration objects or state trees that contain Dates or RegExps. Do not adopt it if you need modern structuredClone semantics, because the library is pinned to a 2018 npm version and its own caveat says sockets and process streams are not cloneable.
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 124 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 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What the library promises, and the one word that sells it

clone offers foolproof deep cloning of objects, arrays, numbers, strings, maps, sets, promises, and other values in JavaScript. The word doing the work in that sentence is foolproof, and it is worth understanding what the library means by it before you rely on it.

The installation is one command:

bash
npm install clone

The package declares no dependencies at all, which is a genuine design constraint and the reason it stays small enough to keep working unchanged for years. It also works with browserify, ender or standalone, and the README notes that you may want the noParse option in browserify to reduce the resulting file size, since usually Buffers are not needed in browsers.

The scope of what it handles is stated plainly. It masters cloning simple objects, even with custom prototype, as well as arrays, Date objects and RegExp objects. Everything is cloned recursively, so you can clone dates in arrays in objects. The example given in the README is the minimum useful case, and it demonstrates the property that matters, that mutating the source leaves the copy untouched:

javascript
var clone = require('clone');

var a, b;

a = { foo: { bar: 'baz' } };  // initial value of a

b = clone(a);                 // clone a -> b
a.foo.bar = 'foo';            // change a

The printed output is shown as two objects, the mutated one and the untouched clone, which is the whole point of a deep copy and the reason a shallow spread operator is not enough.

Circular references are handled by default, which is the unusual choice

Most hand-rolled deep copy functions break on a self-referencing object, and most libraries make you opt in to handling it. clone makes it the default.

The README shows the case directly:

javascript
var a, b;

a = { hello: 'world' };

a.myself = a;
b = clone(a);

The printed result is an object with a Circular marker, and the explanation is the important part: b.myself points to b, not to a. The copy is self-consistent rather than a structure that points back into the original.

The cost of that default is a performance decision, and the API makes it explicit. The circular parameter is a boolean that defaults to true. Call clone with circular set to false if you are certain that obj contains no circular references, and the documentation says this will give better performance if needed. There is also no error if undefined or null is passed as the value.

So the default is the safe choice and the fast choice is opt-in, which is the right way round for a general-purpose utility. The only thing to be aware of is that tracking circular references costs something on every call, and on a hot path cloning small objects in a loop that is a real consideration.

The other parameters follow the same pattern of sensible defaults. Depth is optional and defaults to Infinity, so the whole structure is copied unless you want a bounded copy. The prototype parameter sets the prototype to be used when cloning an Object, and it defaults to the __proto__ of the value being cloned, meaning the clone has the same prototype as the original.

The options object, non-enumerable properties, and the prototype clone

The full signature is clone(value[, circular[, depth[, prototype[, includeNonEnumerable]]]]]), and every one of those parameters can also be passed as a single opts object instead. The README gives the parameter list explicitly, and it is a richer surface than most people expect from a helper this small.

The fifth parameter is the one that catches people out. includeNonEnumerable is a boolean, defaulting to false, and setting it to true clones the non-enumerable properties as well, with the caveat that non-enumerable properties on the prototype chain are ignored. That distinction matters: a class instance with a non-enumerable own property loses it by default, and the fix is a flag rather than a rewrite of your data model.

There is also a separate exported function, clone.clonePrototype(obj), which does a prototype clone as described by Oran Looney. That is a separate operation from the main clone, and it exists because changing a prototype is a different problem from copying an object, and a much rarer one.

The parameter list also documents what the library is explicit about: value is the value you want to clone, any type allowed, and the docs are careful to say the value, not the object. That wording tracks with the promise support, since a Promise is a value you might legitimately want to pass through the function.

The one documented gap is the caveat section. Some special objects like a socket or process.stdout/stderr are known to not be cloneable, and the README asks you to open an issue if you find other objects that cannot be cloned. That is the honest limit of the library, and it is a real one for Node code that holds streams.

A changelog that ends in 2018, and a security fix worth knowing about

The version history in the README is short and mostly old, and reading it tells you what changed and when the project effectively stopped.

Version 2.1.2 is dated 2018-03-21 and switched to Buffer.allocUnsafe() on Node 4.5.0 and above, contributed by @ChALkeR. Version 2.1.1, dated 2017-03-09, fixed a build badge in the README and added support for cloning Maps and Sets on Internet Explorer. Version 2.1.0, dated 2016-11-22, added support for cloning Errors, excluded non-enumerable symbol-named object properties from cloning, and added the option to include non-enumerable own properties. Version 2.0.0, dated 2016-09-28, added support for cloning ES6 Maps, Sets, Promises and Symbols.

The 1.x line, kept in parallel for older Node versions, carries the same Buffer change backported to v1.0.4. And v1.0.3, dated 2017-11-08, closed an XSS vulnerability in the npm package which included the file test-apart-ctx.html, disclosed by Juho Nurminen of 2NS, Second Nature Security. That file is still present in the repository root today, alongside test.html, which is a small reminder that a published package and a repository are different things with different histories.

On maintenance, the last push to the repository was on 2026-06-21 and the repository is not archived, but there are no GitHub releases and the package version in package.json is still 2.1.2. The code has been touched without the version moving, which usually means maintenance rather than new work. For a library whose entire job is a recursive copy, that is a reasonable place to be.

What you get for the dependency budget, and the browser story

The implementation lives in a single file. The repository top level contains clone.js as the main entry, package.json, the README, the LICENSE, a .travis.yml, a .npmignore and .gitignore, a docs/ directory, and the two test html files. package.json names clone.js as main, and the dependencies object is empty.

That single-file shape is the practical argument for the library. A deep copy utility has to be reachable from the deepest part of your dependency tree, and every transitive dependency it drags in is a package that has to be audited, versioned and shipped. An empty dependencies object removes that problem entirely.

The browser story is handled explicitly rather than as an afterthought. The README says it works with browserify, ender or standalone, and points out that since usually Buffers are not needed in browsers you may want the noParse option in browserify to reduce the resulting file size. The mention of Buffers is a hint about the implementation, which has to handle Node Buffer objects as well as browser values.

Testing is run with a single command:

bash
npm test

The engine requirement in package.json is node >=0.8, which tells you the code sticks to a very old baseline of the language. The node requirement being that low is not a recommendation to run it on an old Node, it is evidence that the implementation avoids modern syntax, which in turn is why the same file works in a browser bundle without a transpiler step.

When a deep copy library is the wrong answer

There is a case for using this library and a case for not using it, and the case against is more common than people expect.

If the value you are copying is a plain data structure, the modern platform may already do the job. Structured cloning and the spread operator handle a lot of shallow cases, and a JSON round trip handles plain serialisable data. The cases where clone earns its place are the ones where those approaches break: a Date or a RegExp nested several levels down, a Map or a Set, a custom prototype, an Error, a circular structure, or a non-enumerable own property that you need to preserve.

The second case for not using it is scale. If you are cloning on a hot path, the circular tracking that is on by default is work you may not need, and the library itself points you at turning it off. If you are cloning very large trees, a depth limit is available, and it defaults to Infinity, so the cost is proportional to the whole structure.

The third case is a modern runtime requirement. The npm version is 2.1.2 from 2018, the code targets node >=0.8, and there is no GitHub release history to show newer work. If your project needs a maintained dependency with a security response process, this library is not that.

On the licence, it is MIT, with the copyright held by Paul Vorbach and contributors, and the standard text is reproduced in full in the README. That is the permissive licence you want for a utility that ends up in everything, and it is one of the reasons the package has stayed dependency-free in practice.

Editorial conclusion

Adopt clone if you need a deep copy that preserves prototypes and handles circular references without writing a recursive copier yourself, particularly when you are copying configuration objects or state trees that contain Dates or RegExps. Do not adopt it if you need modern structuredClone semantics, because the library is pinned to a 2018 npm version and its own caveat says sockets and process streams are not cloneable. Before you add it, check that the objects you intend to copy are plain data, decide whether the default circular handling costs you more than you need, since disabling it is documented as faster, and note that the last push to the repository was on 2026-06-21.

Frequently asked questions

How do I install clone and make a deep copy?

Run npm install clone, then require it and call it with the value you want copied. The result is recursive, so nested objects, arrays, Dates and RegExps are all copied rather than shared.

Does clone handle circular references?

Yes, by default, since the circular option defaults to true. The copy points at itself rather than back into the original, so b.myself points to b and not to a. Setting circular to false skips that work and gives better performance if you are certain there are no cycles.

How do I clone non-enumerable properties in clone?

Set the includeNonEnumerable option to true, either as the fifth positional argument or in the options object. Note that non-enumerable properties on the prototype chain are ignored in either case.

What cannot be cloned with clone?

The README states that some special objects like a socket or process.stdout/stderr are known to not be cloneable, and asks that other failures be reported as issues.

Official sources

  1. Issues
  2. License: MIT
  3. Project website
  4. pvorb/clone on GitHub
  5. README
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.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/pvorb-clone.svg)](https://hysenlabs.com/projects/pvorb-clone)

Was this useful?