Slug Regular Expression – Free, Online, Private
Matches a lowercase slug of letters, digits, and single hyphens, one per line.
- client-side-converters
- a
The pattern
A slug here is one or more lowercase letters or digits, then zero or more groups of a hyphen plus more letters or digits. client-side-converters matches. A single letter matches. A leading hyphen, a trailing hyphen, an uppercase letter, and a space do not. The pattern is anchored and the m flag makes ^ and $ apply to each line, which is why the tester can show several candidates at once.
The source is ^[a-z0-9]+(?:-[a-z0-9]+)*$. The flags on this page are m. i means case-insensitive. m means ^ and $ match at line breaks, which is how the sample can show several candidates. g is not set. The tester finds non-overlapping matches, and without g a pattern with no anchor still reports the matches it walks. The box under the title is a tester with this pattern and the sample already filled in. Edit either one. Nothing you type is uploaded. The same tester, without a preset, is the regex tool.
How to read it
^ and $ are the start and end of a line because m is set. [a-z0-9]+ is the first segment. (?:-[a-z0-9]+) is a non-capturing group: a hyphen and then another segment. * repeats that group. The hyphen is outside the character class that follows it, so it cannot double up. A segment cannot be empty, which is why -- fails and a trailing hyphen fails. Underscores are not included. Add them to both classes if your routes use snake case.
What it will not do
The pattern is ASCII on purpose. Accented letters and CJK characters do not match. Transliterate first if you want a URL slug from a title in another language. The pattern does not limit length. A two-hundred-character slug matches and is a bad URL. Add an upper bound, {1,60} or similar, on the whole pattern if you need one. The m flag is for the tester. In a language where you test a single string, you can drop m and keep the anchors.
A match is not a parsed value. An email pattern does not send mail, a UUID pattern does not allocate an id, and a password pattern does not store a password. When the next step needs a real parser, use one: the URL constructor, a date library, a semver library, or the converters on this site. The expression is the part you can see.
JavaScript, Python, and Go
The JavaScript snippet uses the RegExp constructor so the source does not have to be escaped for a literal slash. The Python snippet uses a normal string with the same escapes JSON would emit, which keeps \d as a regex digit class. Go's snippet uses a raw string. If the pattern contains a lookahead, the Go snippet says so and does not call MustCompile, because RE2 would reject it at runtime.
JavaScriptconst re = new RegExp("^[a-z0-9]+(?:-[a-z0-9]+)*$", "m");
const found = text.match(re);
console.log(found ? found[0] : "no match");
Pythonimport re
pattern = re.compile("^[a-z0-9]+(?:-[a-z0-9]+)*$", re.MULTILINE)
found = pattern.search(text)
print(found.group(0) if found else "no match")
Gopackage main
import (
"fmt"
"regexp"
)
func main() {
text := ""
re := regexp.MustCompile(`(?m)^[a-z0-9]+(?:-[a-z0-9]+)*$`)
fmt.Println(re.FindString(text))
}
Frequently asked questions
Are underscores allowed?
No. The pattern is hyphenated lowercase. Add underscores to both character classes if you need them.
Does a single word match?
Yes. The hyphen groups are optional. a and tools both match.
Why do anchors need the m flag here?
The sample is several lines. m makes ^ and $ match each line. A single-field check can omit m.
Are uppercase letters matched?
No. Normalize to lowercase before you test, or the line is not a slug by this rule.