natural-compare vs compare-versions vs natural-orderby vs string-natural-compare
Choosing the Right Sorting and Comparison Strategy for Versioning and Filenames
natural-comparecompare-versionsnatural-orderbystring-natural-compareSimilar Packages:

Choosing the Right Sorting and Comparison Strategy for Versioning and Filenames

compare-versions, natural-compare, natural-orderby, and string-natural-compare are specialized utilities for sorting and comparing data that standard JavaScript logic handles poorly.

compare-versions is a strict semantic versioning parser designed to validate and order software versions (e.g., 1.0.0 vs 1.0.0-beta).

natural-compare, natural-orderby, and string-natural-compare solve the "human sorting" problem where numbers inside strings should be treated as values (e.g., ensuring file2 comes before file10), but they differ in their API design, maintenance status, and specific use cases like array sorting versus simple string comparison.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
natural-compare105,205,833113-010 years agoMIT
compare-versions12,577,03763855.5 kB92 years agoMIT
natural-orderby4,643,2236772.5 kB182 years agoMIT
string-natural-compare052-17 years agoMIT

Sorting Logic Deep Dive: Versioning vs. Human-Readable Order

In frontend development, sorting data seems trivial until you encounter edge cases. Standard JavaScript sorting treats numbers as characters ("10" comes before "2") and struggles with software versions ("1.0.0-rc.1" vs "1.0.0"). The packages compare-versions, natural-compare, natural-orderby, and string-natural-compare address these specific gaps, but they serve very different architectural needs.

📦 The Core Distinction: SemVer vs. Natural Order

Before diving into code, it is critical to understand that compare-versions solves a completely different problem than the other three.

compare-versions is a strict parser for Semantic Versioning (SemVer). It understands that 1.0.0-beta is older than 1.0.0. It does not care about "file 2" vs "file 10"; it cares about major, minor, and patch integers.

natural-compare, natural-orderby, and string-natural-compare implement Natural Sort Order. They ensure that strings containing numbers are sorted by numeric value (2 < 10) rather than ASCII character code (1 < 2). These are used for filenames, user-generated lists, and IDs.

🔢 Handling Semantic Versions

When dealing with software releases, native comparison fails because it doesn't understand pre-release hierarchy.

compare-versions provides a dedicated API to compare, satisfy, and validate SemVer strings.

import { compare, validate } from 'compare-versions';

// Correctly identifies that release candidate is older than final release
const result = compare('1.0.0-rc.1', '1.0.0'); 
// Returns -1 (meaning first argument is smaller)

// Validates strict SemVer format
const isValid = validate('1.0.0'); // true
const isInvalid = validate('v1.0'); // false (strict by default)

natural-orderby, string-natural-compare, and natural-compare are not suitable for this. They treat the hyphen and letters as generic characters. They might sort 1.0.0-rc after 1.0.0 depending on ASCII values, which is a critical bug in update logic.

// ❌ DO NOT use natural sort for versions
// These libraries lack SemVer awareness
import naturalCompare from 'string-natural-compare';

// Unreliable result for versioning
naturalCompare('1.0.0-rc', '1.0.0'); 

📂 Sorting Filenames and User Lists

This is where the "Natural Sort" libraries shine. If you display a list of files like img1.png, img2.png, img10.png, standard sorting breaks the user experience.

string-natural-compare offers a simple comparator function ideal for Array.prototype.sort.

import naturalCompare from 'string-natural-compare';

const files = ['img12.png', 'img2.png', 'img1.png'];

// Sorts correctly: img1, img2, img12
files.sort(naturalCompare);

// Supports case-insensitive option
files.sort((a, b) => naturalCompare(a, b, { caseInsensitive: true }));

natural-orderby is built for sorting arrays of objects, similar to how you might use lodash.

import naturalOrderBy from 'natural-orderby';

const items = [
  { name: 'Chapter 10', page: 45 },
  { name: 'Chapter 2', page: 12 },
  { name: 'Chapter 1', page: 5 }
];

// Sorts by the 'name' key using natural logic
const sorted = naturalOrderBy(items, ['name'], ['asc']);
// Result: Chapter 1, Chapter 2, Chapter 10

natural-compare provides the raw algorithm but requires you to wrap it yourself. It is the foundation upon which others were built but lacks the modern conveniences.

import naturalCompare from 'natural-compare';

const list = ['z11', 'z2'];

// Manual wrapper needed for array sorting
list.sort((a, b) => naturalCompare(a, b));

🛠️ API Ergonomics and Flexibility

The developer experience varies significantly between these tools, especially when dealing with real-world messy data.

natural-orderby stands out for handling null and undefined values gracefully, which is common in frontend data fetching.

import naturalOrderBy from 'natural-orderby';

const data = [
  { id: 'b2' },
  { id: null }, // Handles nulls without crashing
  { id: 'b10' }
];

// Automatically pushes nulls to the end (configurable)
naturalOrderBy(data, ['id'], ['asc']);

string-natural-compare allows fine-tuning of the comparison behavior, such as treating numbers as hex or ignoring leading zeros, via options passed directly to the comparator.

import naturalCompare from 'string-natural-compare';

// Compare with specific options
const result = naturalCompare('01', '1', { 
  caseInsensitive: true, 
  ignoreLeadingZeros: true 
});

compare-versions supports loose matching for ranges, a feature unique to version management.

import { satisfies } from 'compare-versions';

// Check if a version falls within a range
const isCompatible = satisfies('1.4.5', '>=1.4.0 <2.0.0'); // true

⚠️ Maintenance and Deprecation Status

A critical architectural decision factor is the long-term viability of the dependency.

natural-compare is effectively deprecated. The repository is archived, and it receives no updates. While the code works, it lacks TypeScript definitions and modern ES module exports. Using it introduces technical debt.

// ❌ Risk: Archived project, no active maintenance
import naturalCompare from 'natural-compare'; 

compare-versions, natural-orderby, and string-natural-compare are actively maintained. They offer modern TypeScript support, tree-shakable ES modules, and regular bug fixes.

// ✅ Safe: Active maintenance and modern types
import { compare } from 'compare-versions';
import naturalOrderBy from 'natural-orderby';
import naturalCompare from 'string-natural-compare';

🌐 Real-World Implementation Scenarios

Scenario 1: Building a Changelog Component

You need to display release notes from newest to oldest.

  • Choice: compare-versions
  • Reason: You must respect SemVer rules. v2.0.0-beta must appear before v2.0.0.
import { compare } from 'compare-versions';

releases.sort((a, b) => compare(b.version, a.version)); // Descending

Scenario 2: File Explorer UI

You are rendering a list of uploaded documents (Report 1.pdf, Report 10.pdf).

  • Choice: string-natural-compare or natural-orderby
  • Reason: Users expect numerical order. If data is simple strings, string-natural-compare is lighter. If data is objects with metadata, natural-orderby is cleaner.
// For simple string arrays
files.sort(naturalCompare);

// For complex objects
naturalOrderBy(files, ['filename'], ['asc']);

Scenario 3: Dependency Dashboard

Your tool checks if installed packages match the package.json requirements.

  • Choice: compare-versions
  • Reason: Only this library understands range syntax like ^ and ~.
import { satisfies } from 'compare-versions';

const isMatch = satisfies(installedVersion, requiredRange);

📊 Summary Comparison

Featurecompare-versionsnatural-orderbystring-natural-comparenatural-compare
Primary UseSemVer LogicObject SortingString SortingLegacy String Sort
SemVer Aware✅ Yes❌ No❌ No❌ No
Natural Numbers❌ No✅ Yes✅ Yes✅ Yes
Object Support❌ No✅ Built-in❌ Manual❌ Manual
Maintenance✅ Active✅ Active✅ Active⚠️ Archived
TypeScript✅ Excellent✅ Excellent✅ Good❌ Poor/None

💡 Final Recommendation

Your choice depends entirely on the data type you are processing.

If you are working with software versions, compare-versions is the only correct choice. It is the industry standard for handling SemVer logic in JavaScript and prevents subtle bugs in update mechanisms.

If you are sorting user-facing lists (filenames, chapters, IDs), avoid the archived natural-compare. Instead, pick based on your data structure:

  • Use string-natural-compare for lightweight, primitive string arrays.
  • Use natural-orderby for robust sorting of object arrays, especially when dealing with null values or multiple sort keys.

By selecting the right tool, you ensure your application behaves predictably whether it is managing dependencies or displaying a simple file list.

How to Choose: natural-compare vs compare-versions vs natural-orderby vs string-natural-compare

  • natural-compare:

    Avoid choosing natural-compare for new projects. While it pioneered the algorithm for human-friendly string sorting, it is effectively archived and no longer maintained. Its functionality has been superseded by more robust, actively maintained alternatives that offer better TypeScript support and modern API patterns.

  • compare-versions:

    Choose compare-versions exclusively for software versioning tasks where strict adherence to Semantic Versioning (SemVer) is required. It is the only tool in this list capable of correctly parsing pre-release tags (like -alpha, -rc) and build metadata. Use it when building update checkers, dependency resolvers, or changelog generators where 2.0.0 must strictly be greater than 1.9.9.

  • natural-orderby:

    Choose natural-orderby when you need to sort complex arrays of objects based on multiple keys with natural number awareness. It is the best fit for frontend data grids, file explorers, or lists where users expect Item 10 to appear after Item 2. Its API mirrors lodash's orderBy, making it familiar to many developers.

  • string-natural-compare:

    Choose string-natural-compare if you need a lightweight, drop-in replacement for the native localeCompare or simple subtraction comparators. It is ideal for quick implementations where you only need to sort an array of primitive strings and do not require the overhead of a full object-sorting library.

README for natural-compare

@version    1.4.0
@date       2015-10-26
@stability  3 - Stable

Natural Compare – Build Coverage

Compare strings containing a mix of letters and numbers in the way a human being would in sort order. This is described as a "natural ordering".

Standard sorting:   Natural order sorting:
    img1.png            img1.png
    img10.png           img2.png
    img12.png           img10.png
    img2.png            img12.png

String.naturalCompare returns a number indicating whether a reference string comes before or after or is the same as the given string in sort order. Use it with builtin sort() function.

Installation

  • In browser
<script src=min.natural-compare.js></script>
  • In node.js: npm install natural-compare-lite
require("natural-compare-lite")

Usage

// Simple case sensitive example
var a = ["z1.doc", "z10.doc", "z17.doc", "z2.doc", "z23.doc", "z3.doc"];
a.sort(String.naturalCompare);
// ["z1.doc", "z2.doc", "z3.doc", "z10.doc", "z17.doc", "z23.doc"]

// Use wrapper function for case insensitivity
a.sort(function(a, b){
  return String.naturalCompare(a.toLowerCase(), b.toLowerCase());
})

// In most cases we want to sort an array of objects
var a = [ {"street":"350 5th Ave", "room":"A-1021"}
        , {"street":"350 5th Ave", "room":"A-21046-b"} ];

// sort by street, then by room
a.sort(function(a, b){
  return String.naturalCompare(a.street, b.street) || String.naturalCompare(a.room, b.room);
})

// When text transformation is needed (eg toLowerCase()),
// it is best for performance to keep
// transformed key in that object.
// There are no need to do text transformation
// on each comparision when sorting.
var a = [ {"make":"Audi", "model":"A6"}
        , {"make":"Kia",  "model":"Rio"} ];

// sort by make, then by model
a.map(function(car){
  car.sort_key = (car.make + " " + car.model).toLowerCase();
})
a.sort(function(a, b){
  return String.naturalCompare(a.sort_key, b.sort_key);
})
  • Works well with dates in ISO format eg "Rev 2012-07-26.doc".

Custom alphabet

It is possible to configure a custom alphabet to achieve a desired order.

// Estonian alphabet
String.alphabet = "ABDEFGHIJKLMNOPRSŠZŽTUVÕÄÖÜXYabdefghijklmnoprsšzžtuvõäöüxy"
["t", "z", "x", "õ"].sort(String.naturalCompare)
// ["z", "t", "õ", "x"]

// Russian alphabet
String.alphabet = "АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюя"
["Ё", "А", "Б"].sort(String.naturalCompare)
// ["А", "Б", "Ё"]

External links

Licence

Copyright (c) 2012-2015 Lauri Rooden <lauri@rooden.ee>
The MIT License