filenamify, sanitize-filename, and slugify are utilities designed to clean and normalize strings, but they serve distinct purposes in web architecture. filenamify focuses on creating valid filesystem filenames while preserving readability and case. sanitize-filename prioritizes security by stripping dangerous characters and preventing path traversal attacks. slugify transforms text into URL-friendly slugs, enforcing lowercase and replacing spaces with separators for web addresses.
When handling user input for files or URLs, raw strings are rarely safe. Special characters, reserved words, and path separators can break filesystems, expose security vulnerabilities, or ruin URL structures. filenamify, sanitize-filename, and slugify solve these problems, but they approach them from different angles. Let's compare how they handle real-world data.
The fundamental difference lies in how each package treats invalid characters. filenamify replaces them to keep the name intact. sanitize-filename removes them to ensure safety. slugify transforms the entire string for web usage.
filenamify replaces invalid characters with a safe substitute (default _). It preserves case and most punctuation that is safe.
import filenamify from 'filenamify';
// Input: "Project: Alpha (Final).docx"
const safe = filenamify('Project: Alpha (Final).docx');
// Output: "Project_ Alpha (Final).docx"
sanitize-filename removes invalid characters entirely. It does not replace them, which can shorten the string unexpectedly.
import sanitize from 'sanitize-filename';
// Input: "Project: Alpha (Final).docx"
const safe = sanitize('Project: Alpha (Final).docx');
// Output: "Project Alpha (Final).docx"
slugify converts the string to lowercase and replaces spaces/symbols with a separator (default -). It removes most punctuation.
import slugify from 'slugify';
// Input: "Project: Alpha (Final).docx"
const safe = slugify('Project: Alpha (Final).docx');
// Output: "project-alpha-final-docx"
Security is critical when accepting filenames from users. Attackers might try to include path separators to write files outside the intended directory.
filenamify focuses on filename validity, not path security. It does not automatically strip path separators like / or \.
import filenamify from 'filenamify';
// Input: "../../etc/passwd"
const safe = filenamify('../../etc/passwd');
// Output: ".._.._etc_passwd" (Still contains dots, may need extra logic)
sanitize-filename is built specifically to prevent path traversal. It strips path separators and reserved names aggressively.
import sanitize from 'sanitize-filename';
// Input: "../../etc/passwd"
const safe = sanitize('../../etc/passwd');
// Output: ".._.._etc_passwd" (Strips path separators effectively)
slugify removes most special characters, including slashes, but it is not designed for security auditing.
import slugify from 'slugify';
// Input: "../../etc/passwd"
const safe = slugify('../../etc/passwd');
// Output: "etc-passwd" (Removes dots and slashes, but not security-hardened)
β οΈ Warning: Never rely solely on these packages for security. Always validate paths on the server and use safe join methods.
Different operating systems have different rules. Windows reserves names like CON, PRN, and AUX. filenamify and sanitize-filename handle these; slugify does not.
filenamify detects Windows reserved names and adds a suffix to make them valid.
import filenamify from 'filenamify';
// Input: "CON.txt"
const safe = filenamify('CON.txt');
// Output: "CON!.txt" (Modifies reserved name)
sanitize-filename also handles reserved names by appending an underscore or similar modification.
import sanitize from 'sanitize-filename';
// Input: "CON.txt"
const safe = sanitize('CON.txt');
// Output: "CON_.txt" (Modifies reserved name)
slugify ignores OS reserved names because it targets URLs, not filesystems.
import slugify from 'slugify';
// Input: "CON.txt"
const safe = slugify('CON.txt');
// Output: "con-txt" (Lowercases, does not handle Windows reservation)
Developers often need to tweak how sanitization happens. filenamify and slugify offer options; sanitize-filename is opinionated.
filenamify allows you to set the replacement character and max length.
import filenamify from 'filenamify';
filenamify('file:name', { replacement: '-', maxLength: 10 });
// Output: "file-name"
sanitize-filename has no configuration options. It runs with fixed security rules.
import sanitize from 'sanitize-filename';
// No options available
sanitize('file:name');
// Output: "filename"
slugify offers extensive options for locale, lowercase toggling, and custom removal patterns.
import slugify from 'slugify';
slugify('file:name', { lower: false, replacement: '_' });
// Output: "file_name" (Preserves case)
You need to save a report generated by the user with their custom title.
filenamifyconst filename = filenamify(`${userTitle}.pdf`);
downloadUrl(blob, filename);
You accept files from an API endpoint and store them on disk.
sanitize-filenameconst safeName = sanitize(req.file.originalname);
fs.writeFileSync(path.join(uploadDir, safeName), buffer);
You need a clean URL for a new article based on its title.
slugifyconst urlSlug = slugify(postTitle);
router.get(`/posts/${urlSlug}`, handler);
| Feature | filenamify | sanitize-filename | slugify |
|---|---|---|---|
| Primary Goal | Valid Filenames | Security & Safety | URL Slugs |
| Case Handling | Preserves Case | Preserves Case | Forces Lowercase |
| Invalid Chars | Replaces (e.g., _) | Removes | Replaces (e.g., -) |
| Path Traversal | No | Yes | Partial |
| Windows Reserved | Yes | Yes | No |
| Config Options | Yes | No | Yes |
These tools solve different problems despite similar inputs. Use filenamify for client-side downloads where user experience matters. Use sanitize-filename for server-side storage where security is critical. Use slugify for routing and web addresses. Never mix them up β using slugify for filenames loses case information, and using filenamify for URLs might leave unsafe characters.
Choose filenamify when you need to save user-generated files on the client or server while keeping the name readable and safe. It handles OS-specific reserved names and allows you to control replacement characters. This is the best option for download buttons, export features, or any scenario where the original filename structure matters.
Choose sanitize-filename when security is the primary concern, especially on the server side. It aggressively removes illegal characters and path separators to prevent directory traversal attacks. Use this for handling untrusted input where safety outweighs preserving the exact original string format.
Choose slugify when generating URL paths, SEO-friendly identifiers, or database keys. It converts text to lowercase and replaces special characters with dashes, making it unsuitable for preserving case-sensitive filenames. This is the standard choice for routing, permalinks, and human-readable IDs.
Convert a string to a valid safe filename
On Unix-like systems, / is reserved. On Windows, <>:"/\|?* along with trailing periods and spaces are reserved.
This module also removes non-printable control characters (including Unicode bidirectional marks) and normalizes Unicode whitespace.
npm install filenamify
import filenamify from 'filenamify';
filenamify('<foo/bar>');
//=> '!foo!bar!'
filenamify('foo:"bar"', {replacement: 'π΄'});
//=> 'fooπ΄barπ΄'
Convert a string to a valid filename.
Convert the filename in a path to a valid filename and return the augmented path.
import {filenamifyPath} from 'filenamify';
filenamifyPath('foo:bar');
//=> 'foo!bar'
Type: object
Type: string
Default: '!'
String to use as replacement for reserved filename characters.
Cannot contain: < > : " / \ | ? * or control characters.
Type: number
Default: 100
Truncate the filename to the given length.
Only the base of the filename is truncated, preserving the extension. If the extension itself is longer than maxLength, you will get a string that is longer than maxLength, so you need to check for that if you allow arbitrary extensions.
Truncation is grapheme-aware and will not split Unicode characters (surrogate pairs or extended grapheme clusters). If the remaining budget (after accounting for the extension) is smaller than a whole grapheme, the base filename may be truncated to an empty string to avoid splitting.
Systems generally allow up to 255 characters, but we default to 100 for usability reasons.
You can also import filenamify/browser, which only imports filenamify and not filenamifyPath, which relies on path being available or polyfilled. Importing filenamify this way is therefore useful when it is shipped using webpack or similar tools, and if filenamifyPath is not needed.
import filenamify from 'filenamify/browser';
filenamify('<foo/bar>');
//=> '!foo!bar!'