4 min read
I bundled the two common string-case helpers on this box: scule 1.3.0 came out at 570 bytes gzip for six converters, while change-case 5.4.4 came out at 795 bytes. On the same fixtures, camelCase took about 0.91 µs per call on scule versus about 1.26 µs on change-case (~1.4×).
Short answer: pick scule when you want a smaller unjs-style helper for kebab/snake/camel identifiers. Keep change-case when you need space-separated title/sentence/constant cases, or when your inputs are human phrases with spaces. No affiliate links in this post.
| Build (esbuild browser minify) | Minified | gzip -9 |
|---|---|---|
| scule (6 converters) | 1,267 B | 570 B |
| change-case (7 converters) | 1,970 B | 795 B |
| scule camelCase only | 735 B | 412 B |
| change-case camelCase only | 1,285 B | 668 B |
About 1.4× smaller gzip for a comparable core export set. Track client cost the same way as size-limit vs bundlesize. Other unjs helpers in the same size lane show up in pathe vs node:path and defu vs lodash.merge.
| Input | scule camelCase | change-case camelCase |
|---|---|---|
| foo-bar-baz | fooBarBaz | fooBarBaz |
| Already_Mixed-case | alreadyMixedCase | alreadyMixedCase |
| hello world | hello world (unchanged) | helloWorld |
| XMLHttpRequest | xMLHttpRequest | xmlHttpRequest |
| getHTTPResponse | getHTTPResponse | getHttpResponse |
| user_id_42 | userId42 | userId_42 |
Kebab and snake agree on delimiter-separated identifiers. Spaces, acronyms, and trailing digits do not. change-case also ships capitalCase, constantCase, sentenceCase, pathCase, and friends that scule does not. For broader lodash-style utility swaps, see es-toolkit vs lodash.
| Call cost (500k, median of 9, 2 order-reversed runs, ns/op) | scule | change-case |
|---|---|---|
| camelCase | 914 / 878 | 1,259 / 1,225 |
| kebabCase | 890 / 763 | 1,169 / 1,138 |
| snakeCase | 851 / 770 | 1,360 / 1,132 |
| pascalCase | 866 / 859 | 1,253 / 1,255 |
scule won every converter I timed by roughly 1.3–1.6×. That matters for codegen loops and build plugins, not for a handful of className transforms. If your bottleneck is hashing keys instead of casing them, compare ohash vs object-hash. Config and path helpers in the same unjs stack sit next to yaml vs js-yaml and smol-toml vs @iarna/toml.
| Situation | My pick |
|---|---|
| Client bundle, kebab/snake/camel only | scule |
| Nuxt / unjs stack already present | scule |
| Inputs are human phrases with spaces | change-case |
| Need CONSTANT_CASE, sentenceCase, pathCase | change-case |
| Must match change-case acronym/digit quirks in tests | stay on change-case |
Bottom line: scule 1.3.0 was 570 B gzip versus 795 B for change-case 5.4.4, and about 1.4× faster on camelCase on this box. Who should not switch: pipelines that feed space-separated titles into the converter, or suites pinned to change-case's acronym and digit rules. Dependency cleanup next door often pairs with knip vs depcheck.
How this was made: I installed both packages on the ShopperCove box, measured esbuild browser bundles, timed four converters in two order-reversed runs, and compared split/camel outputs on seven fixtures. The JSON results sit next to the scripts. The write-up was drafted with AI help and checked against that output.
Lab evidence
Hands-on on ShopperCove box 6 Oct 2026 ~01:50 IST (8 vCPU Intel Xeon / 15 GB shared Linux, Node 22.20.0). scule 1.3.0 vs change-case 5.4.4, esbuild 0.28.2 browser ESM minify + gzip -9. Size: scule core 1,267/570 B vs change-case 1,970/795 B; camel-only 735/412 vs 1,285/668. Speed (500,000 calls, 9-round median, order-reversed, ns/op): camelCase scule 913.8 / 878.2 vs change-case 1,259.3 / 1,224.7; kebab 890.4 / 763.1 vs 1,168.6 / 1,137.7; snake 851.1 / 769.9 vs 1,360.0 / 1,132.1; pascal 865.6 / 859.1 vs 1,253.2 / 1,254.7. Behavior: scule leaves "hello world" unsplit; change-case → helloWorld; acronym/digit outputs differ on XMLHttpRequest, getHTTPResponse, user_id_42. Not tested: browsers, locale casing, non-ASCII, splitSeparateNumbers options. No affiliate.