Fast and lightweight utility functions to check if a value is a plain object.
npm install @httpx/plain-objectFast and lightweight (~80B) functions to check or assert
that a value is a plain object.
A plain object is a basic JavaScript object, such as {}, { data: [] }, new Object() or Object.create(null).
See how it compares to other libraries.









``bash`
$ npm install @httpx/plain-object
$ yarn add @httpx/plain-object
$ pnpm add @httpx/plain-object
- π Provide isPlainObject and assertPlainObject functions.
- π¦ Convenience PlainObject typescript typings.
- π Faster than most alternatives, see benchmarks.
- π Lightweight (starts at ~80B)
- π‘οΈ Tested on node 20-25, bun, browser, cloudflare workers and runtime/edge.
- π Works cross-realms (node:vm runInNewContext,...)
- ποΈ Available in ESM and CJS formats.
π Official website or GitHub Readme
`typescript
import { isPlainObject } from '@httpx/plain-object';
// β
π True
isPlainObject({ }); // β
isPlainObject({ key: 'value' }); // β
isPlainObject({ key: new Date() }); // β
isPlainObject(new Object()); // β
isPlainObject(Object.create(null)); // β
isPlainObject({ nested: { key: true} }); // β
isPlainObject(new Proxy({}, {})); // β
isPlainObject({ [Symbol('tag')]: 'A' }); // β
// β
π (node context, workers, ...)
const runInNewContext = await import('node:vm').then(
(mod) => mod.runInNewContext
);
isPlainObject(runInNewContext('({})')); // β
// βπ False
class Test { };
isPlainObject(new Test()) // β
isPlainObject(10); // β
isPlainObject(null); // β
isPlainObject('hello'); // β
isPlainObject([]); // β
isPlainObject(new Date()); // β
isPlainObject(new Uint8Array([1])); // β
isPlainObject(Buffer.from('ABC')); // β
isPlainObject(Promise.resolve({})); // β
isPlainObject(Object.create({})); // β
isPlainObject(new (class Cls {})); // β
// β οΈ Edge cases
//
// π globalThis isn't properly portable across all JS environments
//
isPlainObject(globalThis); // β with Bun β otherwise (browser, Nodejs, edge, cloudflare)
// π Static built-in classes aren't properly checked. This is a trade-off
// to maintain the best performance and size. If you need to check for these,
// use a custom type guard. But in most cases, you won't need to check for these
// as the probability of writing a code that receives these as plain objects is low.
// and probably indicates an issue in your code.
isPlainObject(Math); // β οΈβ
return true, but Math is not a plain object
isPlainObject(JSON); // β οΈβ
return true, but JSON is not a plain object
isPlainObject(Atomics); // β οΈβ
return true, but Atomics is not a plain object
isPlainObject(Reflect); // β οΈβ
return true, but Reflect is not a plain object
`
`typescript
import { assertPlainObject } from '@httpx/plain-object';
import type { PlainObject } from '@httpx/plain-object';
function fn(value: unknown) {
// π Throws new TypeError('Not a PlainObject') if not a plain object
assertPlainObject(value);
// π Throws new TypeError('Custom message') if not a plain object
assertPlainObject(value, 'Custom message');
// π Throws custom error if not a plain object
assertPlainObject(value, () => {
throw new HttpBadRequest('Custom message');
});
return value;
}
try {
const value = fn({ key: 'value' });
// β
Value is known to be PlainObject
assertType
} catch (error) {
console.error(error);
}
`
#### Generic
Γ¬sPlainObject and assertPlainObject accepts a generic to provide type
autocompletion. Be aware that no runtime check are done. If you're looking for
runtime validation, check zod, valibot or other alternatives.
`typescript
import { isPlainObject } from '@httpx/plain-object';
import type { PlainObject } from '@httpx/plain-object';
type CustomType = {
id: number;
data?: {
test: string[];
attributes?: {
url?: string | null;
caption?: string | null;
alternativeText?: string | null;
} | null;
} | null;
};
const value = { id: 1 } as unknown;
if (isPlainObject
// β
Value is a PlainObject with typescript autocompletion
// Note that there's no runtime checking of keys, so they are
// unknown | undefined. They will require unsing ?. to access. unknown | undefined
const url = value?.data?.attributes?.url; // autocompletion works
// β
url is , so in order to use it, you'll need to`
// manually check for the type.
if (typeof url === 'string') {
console.log(url.toUpperCase());
}
}
#### PlainObject
`typescript
import { assertPlainObject } from '@httpx/plain-object';
import type { PlainObject } from '@httpx/plain-object';
function someFn(value: PlainObject) {
//
}
const value = { key: 'value' } as unknown;
assertPlainObject(value);
someFn(value)
`Benchmarks
> Performance is continuously monitored thanks to codspeed.io.
>
> 
`
RUN v3.2.4 /home/sebastien/github/httpx/packages/plain-object
β bench/comparative.bench.ts > Compare calling isPlainObject with 110x mixed types values 6594ms
name hz min max mean p75 p99 p995 p999 rme samples
Β· "@httpx/plain-object": isPlainObject(v) 932,629.62 0.0008 0.7244 0.0011 0.0010 0.0019 0.0021 0.0103 Β±0.55% 466316
Β· "is-plain-obj":"4.1.0": 'isPlainObj(v)' 891,373.92 0.0009 1.0045 0.0011 0.0010 0.0019 0.0022 0.0149 Β±1.00% 445687
Β· "@sindresorhus/is":"7.1.0": 'is.plainObject(v)' 343,401.88 0.0022 1.1724 0.0029 0.0024 0.0052 0.0079 0.0489 Β±1.50% 171706
Β· "es-toolkit":"1.41.0": 'isPlainObject(v)' 776,704.94 0.0010 0.9174 0.0013 0.0011 0.0022 0.0024 0.0242 Β±1.03% 388353
Β· "redux":"5.0.1": 'isPlainObject(v)' 316,795.75 0.0024 0.7996 0.0032 0.0029 0.0053 0.0084 0.0370 Β±0.91% 158461
Β· "is-plain-object":"5.0.0": 'isPlainObject(v)' 374,471.38 0.0021 1.8234 0.0027 0.0027 0.0056 0.0061 0.0272 Β±1.00% 187236
Β· "immer/is-plain-object":"4.2.0": 'isPlainObject(v)' 310,784.72 0.0026 0.4656 0.0032 0.0033 0.0069 0.0081 0.0267 Β±0.51% 155393
Β· lodash-es:"4.17.21": '_.isPlainObject(v)' 12,787.05 0.0662 1.5537 0.0782 0.0766 0.1903 0.2622 0.5214 Β±1.13% 6394
BENCH Summary
"@httpx/plain-object": isPlainObject(v) - bench/comparative.bench.ts > Compare calling isPlainObject with 110x mixed types values`
1.05x faster than "is-plain-obj":"4.1.0": 'isPlainObj(v)'
1.20x faster than "es-toolkit":"1.41.0": 'isPlainObject(v)'
2.49x faster than "is-plain-object":"5.0.0": 'isPlainObject(v)'
2.72x faster than "@sindresorhus/is":"7.1.0": 'is.plainObject(v)'
2.94x faster than "redux":"5.0.1": 'isPlainObject(v)'
3.00x faster than "immer/is-plain-object":"4.2.0": 'isPlainObject(v)'
72.94x faster than lodash-es:"4.17.21": '_.isPlainObject(v)'
> See benchmark file for details.
Bundle size is tracked by a size-limit configuration
| Scenario (esm) | Size (compressed) |
|-------------------------------------------------------------|------------------:|
| import { isPlainObject } from '@httpx/plain-object | ~ 80B |import { assertPlainObject } from '@httpx/plain-object
| | ~ 133B |Both isPlainObject and assertPlainObject
| | ~ 141B |
> For CJS usage (not recommended) track the size on bundlephobia.
| Level | CI | Description |
|--------------|-----|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Node | β
| CI for 20.x, 22.x, 24.x & 25.x. |
| Browser | β
| Tested with latest chrome (vitest/playwright) |
| Browserslist | β
| > 95% on 01/2025. defaults, chrome >= 96, firefox >= 105, edge >= 113, safari >= 15, ios >= 15, opera >= 103, not dead |
| Bun | β
| Tested with latest (at time of writing >= 1.3.3) |
| Edge | β
| Ensured on CI with @vercel/edge-runtime. |
| Cloudflare | β
| Ensured with @cloudflare/vitest-pool-workers (see wrangler.toml |
| Typescript | β
| TS 5.0 + / are-the-type-wrong checks on CI. |
| ES2022 | β
| Dist files checked with es-check |
| Performance | β
| Monitored with codspeed.io |
> For _older_ browsers: most frontend frameworks can transpile the library (ie: nextjs...)
| Library | Compat | Perf | CJS+ESM |
|------------------------------------------------------------------------|-------------|---------------|---------|
| is-plain-obj | Differences | 1.17x slower | No |
| es-toolkit | No | 1.25x slower | Yes |
| (@redux)isPlainObject | β
100% | 2.76x slower | Yes |
| (lodash)isPlainObject | No | 83.50x slower | No |
100% compatible see tests.
This library wouldn't be possible without @sindresorhus is-plain-obj.
Notable differences:
- [x] Slightly faster (10%) and lighter
- [x] ESM and CJS formats.
- [x] Named export.
- [x] Smaller bundle size.
- [x] Provide a PlainObject type and assertPlainObject function.PlainObject
- [x] Typescript convenience type.
Since v2, it diverges from is-plain-obj by
- [x] Static built-in classes are considered as plain objects (use isStaticBuiltInClass to exclude).
- [x] [Symbol.iterator] is considered as a valid property for plain objects.[Symbol.toStringTag]
- [x] is considered as a valid property for plain objects.
Contributions are welcome. Have a look to the CONTRIBUTING document.
If my OSS work brightens your day, let's take it to new heights together!
Sponsor>), coffee>),
or star β any gesture of support fuels my passion to improve. Thanks for being awesome! πβ€οΈ
![]() | |
JetBrains | Embie.be |
MIT Β© SΓ©bastien Vanvelthem and contributors.