Question

Remove Non-Alphanumeric Characters Except Dashes and Spaces in JavaScript

csharpregex

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 letters
  • a-z — lowercase English letters
  • 0-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.

Take Quiz

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:

PartMeaning
[...]Match one character from a set
^ at the start of [...]Negate the set: match characters not listed
a-zA-ZEnglish lowercase and uppercase letters
0-9Digits
spaceA literal space character
-A literal dash; placing it last avoids treating it as a range operator
gFind 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:

  1. input contains letters, spaces, a dash, #, and !.
  2. The regex checks each character against the allowed set: letters, digits, spaces, and dashes.
  3. # is not allowed, so it matches the pattern.
  4. ! is not allowed, so it also matches the pattern.
  5. The g flag ensures both matches are found.
  6. replace(..., "") replaces each matched character with nothing.
  7. 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.

Take Quiz

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

PatternWhat it preservesWhen to use it
/[^a-zA-Z0-9 -]/gASCII letters, digits, ordinary spaces, dashesSimple English-only labels
/[^a-zA-Z0-9\s-]/gASCII letters, digits, all whitespace, dashesText where tabs or line breaks should remain
/[^\p{L}\p{N} -]/guUnicode letters, Unicode numbers, spaces, dashesInternationalized text
/\W/gWord characters only: letters, digits, underscoreNot 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.

Take Quiz

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 g to replace all unwanted characters.
  • Put - at the end of a character class or escape it as \-.
  • a-zA-Z is 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.

Take Quiz

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.

Take Quiz

Keep learning

Related questions

View more