Username Regex
Validates that a username is 3 to 16 characters long and contains only letters, digits, underscores, or hyphens.
Regex Pattern
^[a-zA-Z0-9_-]{3,16}$Pattern Breakdown
Hover over a token to see what it does.
| Token | Meaning |
|---|---|
| ^ | Anchors the match to the start of the string |
| [a-zA-Z0-9_-] | Character class allowing letters, digits, underscores, and hyphens |
| a-z | Lowercase letters within the character class |
| A-Z | Uppercase letters within the character class |
| 0-9 | Digits within the character class |
| _- | Underscore and literal hyphen (hyphen placed last so it isn't read as a range) |
| {3,16} | Quantifier requiring between 3 and 16 characters total |
| $ | Anchors the match to the end of the string |
Detailed Explanation
What it does
This pattern checks that an entire string is a valid username: only letters, digits, underscores, and hyphens are allowed, and the total length must fall between 3 and 16 characters. Any input containing spaces, punctuation, or an out-of-range length is rejected.
Why it works
The character class [a-zA-Z0-9_-] enumerates every acceptable character in one set, so a single quantified class can validate the whole body of the username. The {3,16} quantifier enforces the length constraint directly inside the character class repetition, and the ^ and $ anchors force the class to consume the entire input rather than just a portion of it.
Common use cases
- Validating sign-up form usernames before submitting to a backend
- Enforcing consistent handle formatting across a platform (chat apps, forums, games)
- Sanitizing usernames used in URLs or file paths (e.g. profile.com/username)
- Client-side input masking to give instant feedback while typing
Edge cases
- Usernames that are purely numeric, like 12345, are allowed unless additional rules are added
- Leading or trailing hyphens/underscores (e.g. _user_) are technically valid under this pattern
- Exactly 3 or exactly 16 characters are valid because the quantifier bounds are inclusive
- Unicode letters (e.g. accented characters or non-Latin scripts) are rejected since only a-z/A-Z are allowed
Limitations
- Does not prevent reserved words (admin, root) or profanity — that requires a separate blocklist check
- Does not enforce that a username starts with a letter, which some systems require
- No support for internationalized usernames without extending the character class with Unicode ranges
Interactive Tester
Edit the pattern or text below — matching runs live in your browser.
Test Cases
Editable — add your own inputs to see if they pass.
| Input | Expected | Result | |
|---|---|---|---|
| Pass | |||
| Pass | |||
| Pass | |||
| Pass | |||
| Pass | |||
| Pass | |||
| Pass | |||
| Pass |
Language Variants
Production-ready examples in 12 languages.
const usernameRegex = /^[a-zA-Z0-9_-]{3,16}$/;
console.log(usernameRegex.test('john_doe')); // trueCommon Mistakes
Forgetting the length quantifier and only checking allowed characters, letting a 1-character or 200-character string through
Fix: Always bound the length with a quantifier like {3,16} rather than validating characters alone
Placing the hyphen in the middle of the character class (e.g. [a-z0-9-_]) where it can accidentally form an unintended range
Fix: Put the hyphen first or last inside the character class, e.g. [a-zA-Z0-9_-], so it's always read literally
Using this pattern to also guarantee uniqueness or reserved-word exclusion
Fix: Combine the regex with a database uniqueness check and a blocklist for reserved names
Performance Notes
- A single bounded character class with {3,16} is O(n) and has no ambiguous repetition, so there is no catastrophic backtracking risk
- Anchoring with ^ and $ lets the engine reject over-length strings quickly without scanning the whole input
- Because the class has no overlapping alternatives, this pattern is safe to run on untrusted user input at scale
Browser Compatibility
| Engine | Supported | Notes |
|---|---|---|
| Chrome | Yes | — |
| Firefox | Yes | — |
| Safari | Yes | — |
| Edge | Yes | — |
| Node.js | Yes | — |