Question
How can I remove every non-alphanumeric character from a JavaScript string while keeping dash (-) and space ( ) characters? For example, how can I clean text without removing letters, numbers, dashes, or spaces?
Short Answer
You will learn how to use JavaScript's String.prototype.replace() with a regular expression to keep only letters, digits, spaces, and dashes. You will also see how character classes, negation, and the global flag work when cleaning text.
Concept
A regular expression (regex) is a pattern used to find text. In this case, the goal is to find every character that is not allowed and replace it with an empty string.
For an ASCII-only rule, the allowed characters are:
A-Z— uppercase English lettersa-z— lowercase English letters0-9— digits- a space — the literal space character
-— a dash
Inside a regex character class ([...]), a leading caret (^) means not. Therefore:
/[^a-zA-Z0-9 -]/g
means “match every character that is not a letter, digit, space, or dash.” Replacing those matches with "" removes them.
This is useful when creating URL-like labels, cleaning imported data, validating simple identifiers, or removing punctuation from user-provided text. The exact allowed-character rule should always match the needs of your application.
Mental Model
Think of the allowed characters as guests on a list at a door:
- Letters, numbers, spaces, and dashes are on the guest list.
- Every other character—such as
!,@,_, or?—is not allowed in.
The regex [^a-zA-Z0-9 -] identifies the people who are not on the list. replace() then removes them from the string.
Syntax and Examples
Use replace() with a negated character class and the global (g) flag:
const cleaned = text.replace(/[^a-zA-Z0-9 -]/g, "");
Example:
const text = "Order #42: Blue-Shirt! (Large)";
const cleaned = text.replace(/[^a-zA-Z0-9 -]/g, "");
console.log(cleaned);
// "Order 42 Blue-Shirt Large"
How the pattern works:
| Part | Meaning |
|---|---|
[...] | Match one character from a set |
^ at the start of [...] | Negate the set: match characters not listed |
a-zA-Z | English lowercase and uppercase letters |
0-9 | Digits |
| space | A literal space character |
- | A literal dash; placing it last avoids treating it as a range operator |
g | Find all matching characters, not only the first one |
If you also want to remove repeated spaces after cleaning, normalize them separately:
const text = "Hello!!! world---42";
const cleaned = text
.replace(/[^a-zA-Z0-9 -]/g, "")
.replace(/ +/g, " ")
.trim();
console.log(cleaned);
// "Hello world---42"
Step by Step Execution
Consider this code:
const input = "Room #7 - East Wing!";
const result = input.replace(/[^a-zA-Z0-9 -]/g, "");
console.log(result);
Step by step:
inputcontains letters, spaces, a dash,#, and!.- The regex checks each character against the allowed set: letters, digits, spaces, and dashes.
#is not allowed, so it matches the pattern.!is not allowed, so it also matches the pattern.- The
gflag ensures both matches are found. replace(..., "")replaces each matched character with nothing.- The result is:
Room 7 - East Wing
Real World Use Cases
Common uses include:
- Cleaning product names: Keep names readable while removing punctuation from imported catalog data.
- Generating simple labels: Convert
"Project: Alpha!"into"Project Alpha". - Filtering form input: Allow a limited character set for a code, reference, or display name.
- Preparing search text: Remove punctuation before comparing simple search terms.
- Creating filenames: Apply a project-specific allowed-character policy before saving a generated file.
For security-sensitive validation, do not assume this cleanup alone makes data safe for every context. For example, database queries should still use parameterized queries, and HTML output should still be escaped appropriately.
Real Codebase Usage
In production code, developers usually put text-cleaning rules in a named function so the policy is easy to reuse and test:
function cleanLabel(value) {
return String(value)
.replace(/[^a-zA-Z0-9 -]/g, "")
.replace(/ +/g, " ")
.trim();
}
console.log(cleanLabel(" Invoice #100! - Paid "));
// "Invoice 100 - Paid"
Useful project patterns include:
- Validation before cleanup: Reject empty values or values that are too long before processing them.
- Normalization: Collapse repeated spaces and trim leading or trailing spaces after removing invalid characters.
- Separate display text from identifiers: A human-readable label can preserve spaces and dashes, while a slug may use stricter rules.
- Explicit policies: Define whether underscores, accented letters, tabs, line breaks, and multiple dashes should be allowed rather than relying on an accidental regex behavior.
For example, a guard clause can prevent unexpected input:
function cleanLabel(value) {
if (typeof value !== "string") {
throw new TypeError("value must be a string");
}
return value.replace(/[^a-zA-Z0-9 -]/g, "");
}
Common Mistakes
Forgetting the g flag
Without g, only the first unwanted character is removed:
const text = "Hi! Are? You#";
console.log(text.replace(/[^a-zA-Z0-9 -]/, ""));
// "Hi Are? You#"
Use g to replace every match:
console.log(text.replace(/[^a-zA-Z0-9 -]/g, ""));
// "Hi Are You"
Putting - in the middle of a character class
A dash can represent a range in a character class. This is confusing or can create an invalid pattern:
// Avoid this style when possible:
/[^a-zA-Z0-9- ]/g
Place - at the end, beginning, or escape it:
/[^a-zA-Z0-9 -]/g
/[^a-zA-Z0-9 \-]/g
Using \W when spaces and dashes must remain
\W means “not a word character,” but word characters include underscores and do not include dashes or spaces. It does not express the desired rule:
"hello-world test".replace(/\W/g, "");
// "helloworldtest"
Use an explicit allowed list instead.
Assuming a-z supports every language
a-zA-Z preserves only basic English letters. It removes characters such as é, ñ, 中文, and مرحبا. If your application supports international text, use a Unicode-aware pattern.
Confusing a space with all whitespace
The pattern shown preserves only the ordinary space character. Tabs and newlines are removed. If you want to preserve all whitespace, use \s:
const cleaned = text.replace(/[^a-zA-Z0-9\s-]/g, "");
Comparisons
| Pattern | What it preserves | When to use it |
|---|---|---|
/[^a-zA-Z0-9 -]/g | ASCII letters, digits, ordinary spaces, dashes | Simple English-only labels |
/[^a-zA-Z0-9\s-]/g | ASCII letters, digits, all whitespace, dashes | Text where tabs or line breaks should remain |
/[^\p{L}\p{N} -]/gu | Unicode letters, Unicode numbers, spaces, dashes | Internationalized text |
/\W/g | Word characters only: letters, digits, underscore | Not suitable when spaces and dashes must remain |
A Unicode-aware version can preserve letters and numbers from many writing systems:
const cleaned = text.replace(/[^\p{L}\p{N} -]/gu, "");
This requires JavaScript environments with Unicode property escape support. The u flag enables Unicode mode.
Cheat Sheet
// Remove everything except ASCII letters, digits, spaces, and dashes
text.replace(/[^a-zA-Z0-9 -]/g, "");
// Also keep tabs and line breaks
text.replace(/[^a-zA-Z0-9\s-]/g, "");
// Keep Unicode letters and numbers too
text.replace(/[^\p{L}\p{N} -]/gu, "");
// Collapse repeated ordinary spaces and trim edges
text
.replace(/[^a-zA-Z0-9 -]/g, "")
.replace(/ +/g, " ")
.trim();
Rules to remember:
[^...]means “anything not in this allowed set.”- Use
gto replace all unwanted characters. - Put
-at the end of a character class or escape it as\-. a-zA-Zis ASCII-only; use\p{L}for Unicode letters.- Replacing characters is not the same as validating a complete value.
FAQ
How do I keep only letters, numbers, spaces, and hyphens in JavaScript?
Use:
text.replace(/[^a-zA-Z0-9 -]/g, "")
Why is the caret (^) used in the regex?
Inside the beginning of a character class, ^ negates the class. It matches characters that are not allowed, so they can be removed.
Does this regex preserve underscores?
No. An underscore is removed because it is not listed as an allowed character. Add _ to allow it:
text.replace(/[^a-zA-Z0-9 _-]/g, "")
Does the pattern preserve tabs and newlines?
No. It preserves only normal spaces. Use \s instead of a literal space if all whitespace should remain.
Why should the dash be last in the character class?
In a character class, - can describe a range, such as a-z. Placing it last makes its literal dash meaning clear.
Will this keep accented letters such as é?
Not with a-zA-Z. For Unicode letters, use:
text.replace(/[^\p{L}\p{N} -]/gu, "")
Should I sanitize user input this way for security?
It can enforce a formatting rule, but it is not a universal security solution. Use context-specific protections, such as parameterized database queries and output escaping.
Mini Project
Description
Build a small JavaScript utility that cleans labels entered by users. It will remove punctuation and symbols, preserve letters, numbers, spaces, and dashes, and produce consistent spacing for display or storage.
Goal
Create a cleanLabel function that returns a trimmed label containing only ASCII letters, numbers, single spaces, and dashes.
Requirements
Accept a string value as input. Remove characters other than ASCII letters, digits, spaces, and dashes. Collapse consecutive spaces into one space. Remove spaces from the beginning and end of the result. Throw a clear error when the input is not a string.
Keep learning
Related questions
AddTransient vs AddScoped vs AddSingleton in ASP.NET Core Dependency Injection
Learn the differences between AddTransient, AddScoped, and AddSingleton in ASP.NET Core DI with examples and practical usage.
Best Way to Repeat a Character in C#: Building Repeated Strings Efficiently
Learn the best way to repeat a character in C#, compare StringBuilder, string concatenation, and simpler built-in options.
C# Access Modifiers and static: public, private, protected, and Defaults
Learn how C# public, private, protected, and default access control visibility, and how static differs from instance members.