filenamify

Convert any string into a valid, cross-platform safe filename in one function call.

Library
npm
v7.0.3
526stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
57/100Fair
Development Activity60
Maintenance36
Community52
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
72/100Good
Architecture78
Code Quality88
Innovation65
Learning Curve55

filenamify is a small utility from Sindre Sorhus that takes any string — user input, a URL, a page title, an API resource name — and converts it into a filename that’s safe to write on both Unix-like systems and Windows. It strips or replaces reserved characters (< > : " / \ | ? *), removes control characters and Unicode formatting marks (while preserving the zero-width joiner needed for emoji), normalizes exotic Unicode whitespace to a plain space, and guards against Windows-reserved device names like CON or NUL by appending a replacement suffix.

Beyond simple character substitution, it handles the edge cases that trip up naive sanitizers: grapheme-aware truncation (via Intl.Segmenter) that won’t split emoji sequences, surrogate pairs, or combining characters when enforcing a maxLength; trailing dot/space stripping per Windows naming rules; and a companion filenamifyPath export that sanitizes only the basename of a full path while leaving the directory portion untouched.

What You Get

  • filenamify(string, options?) — the core function that sanitizes a string into a safe filename, replacing reserved characters with a configurable replacement (default !).
  • filenamifyPath(path, options?) — resolves a path and sanitizes only its basename, then rejoins it with the original directory so the rest of the path is untouched.
  • A browser-only entry point (filenamify/browser) that omits the Node.js path-dependent filenamifyPath export for bundler-friendly usage.
  • Grapheme-aware truncation built on Intl.Segmenter that respects a configurable maxLength (default 100) without splitting emoji, surrogate pairs, or combining-character sequences.
  • Automatic detection and disambiguation of Windows-reserved device names (CON, NUL, PRN, COM1-COM9, LPT1-LPT9, etc.) by appending the replacement string.

Common Use Cases

  • Saving user-generated content - An app that lets users name their own exports (PDFs, images, notes) runs the title through filenamify before writing to disk, so titles containing /, :, or emoji don’t break the write or corrupt the path.
  • Downloading files by remote title - A scraper or download manager that names local files after a webpage title, API resource name, or email subject uses filenamify to strip characters that are illegal on the OS it’s running on.
  • Cross-platform CLI tools - A build tool or CLI that writes one output file per item (per test case, per commit, per record) uses filenamify so the same tool produces valid filenames on Linux/macOS and Windows without OS-specific branching.
  • Caching by derived key - A cache or asset pipeline that derives a filename from a cache key or URL uses filenamify to guarantee the derived name is filesystem-safe regardless of what characters appear in the key.

Under The Hood

Architecture All sanitization logic lives in a single ordered pipeline inside filenamify.js: normalize to NFC, collapse Unicode whitespace, collapse repeated reserved characters, strip trailing dots/spaces, replace relative-path patterns and reserved characters (via the filename-reserved-regex dependency), strip control characters, re-strip trailing dots/spaces, fall back to the replacement string if the result is empty, truncate by grapheme budget, strip trailing dots/spaces again, and finally guard against Windows-reserved device names. filenamify-path.js is a thin wrapper that uses node:path to split a path into dirname/basename, delegates the basename to filenamify(), and rejoins the result — so both exports break identically if the core pipeline changes, by design. index.js is the single Node entry point re-exporting both; a separate filenamify/browser export exists purely to omit the node:path dependency for bundled code.

Tech Stack Pure ESM ("type": "module") targeting Node.js >=20, with exactly one runtime dependency (filename-reserved-regex ^4.0.1) for reserved-character detection. Grapheme splitting uses the built-in Intl.Segmenter rather than a polyfill library. The exports map in package.json splits a Node-facing . entry from a ./browser entry that excludes filenamifyPath. No build or bundling step — it ships hand-written ESM source directly alongside hand-written .d.ts declaration files. Dev tooling is ava for tests and xo (an opinionated ESLint preset) for linting, both run via npm test; CI (GitHub Actions) runs that same command across a Node 20/24 matrix.

Code Quality test.js carries extensive coverage — dozens of ava assertions across the core filenamify() behavior, filenamifyPath(), length/truncation boundary conditions, and dedicated grapheme-aware truncation cases for emoji sequences, surrogate pairs, combining characters, and regional-indicator flag emojis. Linting is enforced in CI via xo. There’s no compiled TypeScript, but hand-written .d.ts files ship with the package so consumers get full type information without a build step. Error handling is explicit rather than silent: a TypeError is thrown for non-string input, and a plain Error for a replacement string that itself contains reserved or control characters.

API Design The public surface is deliberately minimal — one default export (a function) plus one named export for path handling — following the single-purpose, sensible-defaults style typical of Sindre Sorhus’s utility packages. Every option is optional (replacement defaults to !, maxLength defaults to 100), and the README documents exact precedence rules, such as Windows-reserved-name avoidance taking priority over the configured maxLength. The genuinely hard part this library gets right — where many ad hoc sanitizers fail — is Unicode-correct truncation that never splits an emoji or combining-character sequence mid-grapheme.

Used by 6 apps in this directory

TypeScript
72%
AGPL 3.0

APITable

Low Code Platforms · Databases

15,588

API-first collaborative spreadsheet-database platform that auto-generates REST APIs and lets teams build internal tools, CRMs, and dashboards without code.

View details
74
Repo Health
77
Technical
60
Dependency
Built with
TypeScript72%
Java22%
Updated yesterday
Rust
67%
MIT

Bun

Developer Tools

95,895

An all-in-one JavaScript and TypeScript toolkit — one Rust-and-JavaScriptCore binary that replaces Node.js, npm, a bundler, and a test runner with faster equivalents.

View details
92
Repo Health
91
Technical
64
Dependency
Built with
Rust67%
C++19%
Updated yesterday
TypeScript
65%
Other

Epicenter

Knowledge Management · Note Taking · Developer Tools

4,790

A local-first monorepo led by Whispering, an open-source speech-to-text app, built on an MIT toolkit that turns your data into plain Markdown and SQLite files you own instead of a database you rent.

View details
88
Repo Health
90
Technical
68
Dependency
Built with
TypeScript65%
Svelte13%
HTML12%
Updated yesterday
TypeScript
81%
MIT

LibreChat

Developer Tools · AI Assistants

42,871

Unite every major AI model in one self-hosted chat platform with agents, code execution, MCP tools, and enterprise authentication.

View details
93
Repo Health
81
Technical
65
Dependency
Built with
TypeScript81%
JavaScript18%
Updated today
TypeScript
94%
AGPL 3.0

Papra

Bookmarks Archiving

5,296

Self-hosted document archiving with email ingestion, OCR full-text search, and pluggable storage — store once, find anything.

View details
86
Repo Health
82
Technical
69
Dependency
Built with
TypeScript94%
Updated yesterday
TypeScript
97%
Other

Rocket.Chat

Team Chat

46,089

The secure, self-hosted team communications platform for organizations that cannot compromise on data sovereignty.

View details
96
Repo Health
79
Technical
66
Dependency
Built with
TypeScript97%
Updated yesterday

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search