Markdown Link Regex
Matches Markdown link syntax [text](url) and captures the link text and destination, with optional support for a quoted title.
Regex Pattern
\[([^\]]+)\]\(([^)\s]+)(?:\s+"[^"]*")?\)Pattern Breakdown
Hover over a token to see what it does.
| Token | Meaning |
|---|---|
| \[ | Literal opening square bracket that starts the link text |
| ([^\]]+) | Capture group 1: the link text, one or more characters that are not ] |
| \] | Literal closing square bracket ending the link text |
| \( | Literal opening parenthesis that starts the destination |
| ([^)\s]+) | Capture group 2: the URL, one or more characters that are not ) or whitespace |
| (?:\s+"[^"]*")? | Optional non-capturing group for a space-separated quoted title, e.g. "Example" |
| \) | Literal closing parenthesis ending the link |
Detailed Explanation
What it does
This pattern locates inline Markdown links of the form [link text](https://example.com) anywhere in a string and captures the visible text and the target URL as separate groups. It also tolerates an optional title attribute in quotes after the URL, as in [text](url "Title").
Why it works
The two bracket-delimited groups use negated character classes ([^\]]+ and [^)\s]+) instead of greedy wildcards so each group stops precisely at its own closing delimiter without overrunning into the rest of the link or accidentally spanning multiple links. The optional non-capturing group for the title is anchored to whitespace followed by a quoted string, so it only engages when a title is actually present.
Common use cases
- Extracting all links and their labels from a Markdown document for a table of contents
- Rewriting relative links to absolute links during a static site build
- Validating that documentation contains no malformed or empty link syntax
- Converting Markdown links to HTML anchor tags in a lightweight renderer
Edge cases
- Image syntax like  still matches the trailing [alt](image.png) portion since the pattern is unanchored and doesn't check for a preceding !
- Links with a title attribute, e.g. [text](url "Title"), are matched including the title in the full match but the title itself isn't captured separately
- Empty link text such as [] () fails to match because the text group requires at least one non-] character
- URLs containing spaces are not matched because the destination group stops at the first whitespace character
- Nested brackets inside the link text, like [a [b] c](url), will stop capturing at the first ]
Limitations
- Does not validate that the captured URL is itself well-formed; use a dedicated URL pattern for that
- Reference-style links like [text][ref] are not matched, only inline links
- Does not distinguish between a real link and an image link prefixed with !
- Titles using single quotes instead of double quotes are not recognized
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 | |||
| Pass |
Language Variants
Production-ready examples in 12 languages.
const mdLinkRegex = /\[([^\]]+)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g;
const matches = [..."[Docs](https://example.com)".matchAll(mdLinkRegex)];
console.log(matches[0][1], matches[0][2]); // 'Docs' 'https://example.com'Common Mistakes
Using .* instead of [^\]]+ for the link text, causing the match to greedily span across multiple links on the same line
Fix: Use a negated character class scoped to the delimiter (e.g. [^\]]+) so each group stops at its own closing bracket
Forgetting the g flag when extracting all links from a document, so only the first link is found
Fix: Add the g flag (or use matchAll/finditer/scan depending on language) to retrieve every match
Assuming this pattern also matches image syntax  as a distinct type
Fix: Check for a preceding ! character separately if images need to be excluded or handled differently
Performance Notes
- The negated character classes [^\]]+ and [^)\s]+ are efficient because they can only match up to their terminating delimiter, avoiding backtracking blowups
- Running this pattern with the global flag over a large document is linear in the length of the text
- The optional title group only adds overhead when whitespace immediately follows the URL, so plain links are matched with minimal extra work
Browser Compatibility
| Engine | Supported | Notes |
|---|---|---|
| Chrome | Yes | — |
| Firefox | Yes | — |
| Safari | Yes | — |
| Edge | Yes | — |
| Node.js | Yes | — |
Work with this pattern
Take this expression further — test it against your own data, adapt it in the builder, or read up on the syntax behind it.
Regex Tester
Test a regular expression against sample text and see every match highlighted live.
Regex Generator
Build a pattern by clicking blocks — no syntax memorisation required.
Regular Expressions Guide
What regex is, how the syntax works, and the mistakes that trip people up.
Regex Cheat Sheet
Every token, quantifier, and assertion on one printable page.
Python Regex Guide
The re module end to end — search, match, findall, groups, and substitution.
Regex Pattern Library
100+ ready-to-use patterns for email, URLs, dates, IPs, and more.