ramda review
Our Node 22 sandbox installed Ramda 0.32.0 in 1 second with no package dependencies. Ramda is a functional utility collection for plain JavaScript arrays, objects, and functions. Most calls are automatically curried and place the value being transformed last, which lets map, filter, sort, lenses, and object updates compose through pipe or compose. Functions return new values instead of mutating their inputs. Version 0.32.0 improves decimal arguments to range, repairs package exports, and updates examples for head, last, type, and pipeWith.
Ramda earns its space when a JavaScript team consistently uses currying and data-last composition. TypeScript projects and browser entries should compare Remeda or es-toolkit first because Ramda lacks bundled types and our full import measured 15.7 KB gzipped.
We installed it
| Install | ✓ · 1s | 2 packages on disk · 4 MB |
| Import | ✓ | ESM import works · require() works · CommonJS package with exports map |
| Browser | 15.7 KB | gzipped (56.9 KB minified), bundled with esbuild |
| Types | — | no TypeScript types found |
| Known vulns | 0 | 0 critical · 0 high · 0 moderate · 0 low (npm audit) |
Answers from our run
Does ramda install cleanly?
Yes. In a fresh container with an empty cache, npm install ramda finished in 1 seconds, leaving 2 packages and 4 MB on disk. npm audit reported no known vulnerabilities.
How much does ramda add to a browser bundle?
15.7 KB gzipped (56.9 KB minified) when the whole package is bundled for the browser with esbuild. Importing only part of it is usually smaller.
Does ramda work with both ESM and CommonJS?
Yes. Both import 'ramda' and require('ramda') worked in Node 22 in our run. The package is published as CommonJS with an exports map.
Does ramda include TypeScript types?
No type declarations were found in our install, so TypeScript users need their own declarations.
ramda or remeda: which should you use?
remeda: Choose it for data-first and data-last pipelines designed around TypeScript inference. Ramda earns its space when a JavaScript team consistently uses currying and data-last composition.
When should you not use ramda?
The project is TypeScript-first. Ramda 0.32.0 ships no declarations; @types/ramda is maintained separately and complex currying or long pipe chains can exceed its inference.
Use it if
- The team deliberately writes data-last pipelines and wants currying built into ordinary collection and object helpers.
- Nested plain objects need immutable reads and updates through lensPath, view, set, or over without introducing a custom collection type.
- Transformation functions should be partially applied once and reused across several lists or records.
- Code needs structural equality, grouping, indexing, or declarative field evolution beyond the native array methods used elsewhere in the project.
- The project is TypeScript-first. Ramda 0.32.0 ships no declarations; @types/ramda is maintained separately and complex currying or long pipe chains can exceed its inference.
- A full namespace import reaches the browser. Our measured bundle was 56.9 KB minified and 15.7 KB gzipped, so selective imports or a smaller library need checking in the actual bundler.
- Native map, filter, reduce, Object.groupBy, spread, and structuredClone already cover the transformations. Adding a second vocabulary would make common operations harder to read.
- The team does not share the point-free style. Functions such as converge, useWith, and placeholders make debugging and code review slower when only part of the codebase uses them.
- Strict semantic versioning is a release requirement. Ramda remains on 0.x, and its README warns that tracking `latest` can expose callers to API changes.
Setup reality
We installed Ramda 0.32.0 in a fresh Node 22 container in 1 second. Two packages occupied 4 MB afterward. Ramda declares zero direct dependencies and zero peer dependencies; its own unpacked package is 3452 KB and uses the MIT license. npm audit found zero known vulnerabilities. The package uses CommonJS as its base format and supplies an exports map; both require and ESM import worked in our sandbox.
There are no credentials, plugins, native builds, peer setup, or runtime config. Ramda has had no default export since versions after 0.25. Use import * as R from 'ramda', named imports, or const R = require('ramda'). The exports map also exposes ramda/es/, ramda/src/, and ramda/dist/*. Version 0.32.0 includes a package export fix, so older workarounds around subpath resolution deserve retesting before they are kept.
TypeScript is a separate decision. We found no bundled declarations, and the README directs users to @types/ramda. Curried overloads, the placeholder R.__, and long pipe chains are difficult for community declarations to represent precisely. JavaScript users avoid that mismatch. TypeScript teams should compile representative pipelines before choosing Ramda, rather than assuming the simplest map example predicts inference across the application.
Our esbuild test imported the full package namespace and produced 56.9 KB minified, 15.7 KB gzipped. The package marks itself side-effect free, yet the README says destructured imports do not always guarantee a partial bundle and documents direct ramda/src imports plus bundler-specific tree shaking. Measure the production entry. Currying also allocates wrapper functions, so a tight numeric or collection loop should be benchmarked against a native loop or array method.
Patterns
Build a reusable list pipeline compose-pipeline
import {filter, map, pipe, prop} from 'ramda'
const activeNames = pipe(
filter(user => user.active),
map(prop('name')),
)
const names = activeNames(users)pipe runs left to right. The first function may accept several arguments; later functions receive the previous result.
Reuse a partially applied filter partially-apply
import {filter, propEq} from 'ramda'
const onlyActive = filter(propEq(true, 'active'))
const customers = onlyActive(allCustomers)
const staff = onlyActive(allStaff)Ramda's data argument comes last, so leaving it out creates a function waiting for the collection.
Read a nested value with a fallback read-default-path
import {pathOr} from 'ramda'
const currency = pathOr('USD', ['billing', 'currency'], account)pathOr returns the fallback when the resolved value is null or undefined. Other false values such as 0 or an empty string are preserved.
Change a nested field immutably update-nested-path
import {assocPath} from 'ramda'
const updated = assocPath(['profile', 'timezone'], 'UTC', user)
console.log(user.profile.timezone)
console.log(updated.profile.timezone)assocPath copies the containers along the changed path and reuses untouched references. It does not deep-clone the whole object.
Read and update through one lens use-lens
import {lensPath, over, set, view} from 'ramda'
const totalLens = lensPath(['invoice', 'total'])
const total = view(totalLens, order)
const corrected = set(totalLens, 25, order)
const taxed = over(totalLens, value => value * 1.2, order)view reads, set replaces, and over transforms. All three use the same path definition; set and over return new outer objects.
Transform selected object fields evolve-record
import {evolve, toLower, trim} from 'ramda'
const normalized = evolve({
email: value => toLower(trim(value)),
attempts: value => value + 1,
}, record)Fields absent from the transformation object pass through. A transformation for a property missing from the input is not called.
Group rows by a computed key group-records
import {groupBy, prop} from 'ramda'
const byStatus = groupBy(prop('status'), tickets)
const openTickets = byStatus.open ?? []groupBy returns an object of arrays. Property keys are strings, so choose a Map-based approach when key identity or non-string keys matter.
Index records by identifier index-records
import {indexBy, prop} from 'ramda'
const usersById = indexBy(prop('id'), users)
const owner = usersById['42']Later records replace earlier ones that produce the same key. Object keys are coerced to strings.
Apply ordered sort rules sort-multiple-fields
import {ascend, descend, prop, sortWith} from 'ramda'
const ranked = sortWith([
descend(prop('score')),
ascend(prop('name')),
], entries)sortWith returns a sorted copy and leaves the input array untouched. Earlier comparators take precedence over later tie breakers.
Compute one result from several branches combine-derived-values
import {converge, divide, length, sum} from 'ramda'
const average = converge(divide, [sum, length])
const mean = average([4, 8, 12])Each branch receives the same original arguments. Empty input makes this example divide zero by zero and return NaN, so guard that case in production code.
Adapt arguments before a call transform-with-arguments
import {multiply, useWith} from 'ramda'
const areaFromStrings = useWith(multiply, [Number, Number])
const area = areaFromStrings('6', '7')useWith applies one transformer per supplied position. Extra arguments beyond the transformer list pass through unchanged.
Compare nested values structurally compare-structures
import {equals} from 'ramda'
const unchanged = equals(
{tags: ['a', 'b'], when: new Date('2026-01-01')},
{tags: ['a', 'b'], when: new Date('2026-01-01')},
)equals handles many built-in value types and cyclic structures. Its semantics differ from JSON string comparison and strict reference equality.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| remeda | npm | Choose it for data-first and data-last pipelines designed around TypeScript inference. |
| es-toolkit | npm | Choose it for typed, tree-shakeable utility functions when automatic currying is not part of the coding style. |
| rambda | npm | Choose it for a smaller Ramda-like API with built-in TypeScript types, after checking which functions differ or are missing. |
| lodash | npm | Choose it for a broad data-first utility set that fits imperative JavaScript and has extensive existing usage material. |
More utils guides
lru-cache · ajv · type-fest · p-limit · find-up · js-yaml · the whole shelf →
How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.

