On this page
Text Link
An anchor link for navigating to pages or anchor points within prose
- Overview
- Rules and guidance
- Properties
- Styles
- Accessibility
When to use
- For navigation links embedded within body text, such as references, further reading, or related pages
- For standalone navigation links where a full button-style target is not appropriate
- When linking to an anchor point within the same page
When not to use
- To trigger an action such as submitting a form or opening a modal - links navigate; buttons act. Use CTA Button instead.
- As a primary or secondary call to action - use CTA Link instead, which has the visual prominence of a button while remaining an anchor element.
- When the link label would be non-descriptive - do not use "click here", "read more", or "find out more" as link text. Rewrite the surrounding sentence so the link label describes the destination.
Anatomy
- Link text (the visible, underlined label)
- External indicator (an icon and visually hidden text appended when opensInNewWindow is true)
Basic usage
Edit live code
<TextLink href="https://friendsoftheearth.uk/climate-change/live-sustainably-how-be-conscious-consumer" >How to be a conscious consumer</TextLink>
Variants and features
Inline links
TextLink is designed to sit within prose. Place it inside a Text (P) element so it inherits body font size and color context correctly.
The underline is always visible on TextLink. This is intentional: removing underlines from inline links reduces their discoverability for users with colour vision deficiencies.
Edit live code
<P>Visit <TextLink href="https://friendsoftheearth.uk/climate-change/live-sustainably-how-be-conscious-consumer" >Friends of the Earth</TextLink> for more information.</P>
Opening in a new window
Use opensInNewWindow when the link genuinely needs to open in a new browser tab, such as a link to an external site or a document that the user needs to reference while staying on the current page. Do not use it by default. Opening links in new windows removes the user's ability to navigate back and is unexpected behaviour for most links.
When opensInNewWindow is true, the component automatically appends a new-window icon and a visually hidden " (opens in new window)" text disclosure so screen reader users are informed before activating the link.
Edit live code
<TextLink href="https://www.ofgem.gov.uk" opensInNewWindow> Ofgem price cap information </TextLink>
External links
For links to external domains, TextLink automatically adds rel="noopener" to prevent the linked page from accessing the opener's window object. This happens based on whether the href is detected as an external link. No extra configuration is needed.
React Router integration
Pass the React Router Link component via the as prop to use client-side navigation while keeping the visual style of TextLink.
This example is not live, because the docs site does not use React Router.
import { Link } from 'react-router-dom';
<TextLink as={Link} to="/account">
View account
</TextLink>
usage
- TextLink must have an href prop set to a valid URL or anchor referenceTextLink renders an anchor element. An anchor without href is not keyboard-focusable and is not treated as a link by browsers or assistive technology.Copy rule ID for textlink.href-required
- TextLink must not be used to trigger in-page actions such as submitting a form, opening a modal, or toggling stateLinks navigate; buttons act. Using a link for an action breaks the user's mental model and can break keyboard and screen reader interaction. Use CTAButton instead.Copy rule ID for textlink.navigation-not-action
- TextLink must not have its underline removedUnderline is the primary visual differentiator for inline links, especially for users who cannot distinguish by colour alone. Removing it fails WCAG 2.2 SC 1.4.1.Copy rule ID for textlink.underline-always-visible
- opensInNewWindow should not be set to true by default on all external linksOpening links in new tabs removes the user's ability to navigate back and is unexpected unless there is a clear reason (e.g. the user needs to reference a document while completing a task).Copy rule ID for textlink.opens-in-new-window-sparingly
- TextLink should not be used as a primary or secondary call to actionCTALink provides button-style prominence for call-to-action navigation. TextLink is for inline or supplementary links.Copy rule ID for textlink.use-cta-link-for-cta
placement
- TextLink should be placed inside a P element when used as an inline prose linkTextLink inherits font size from its parent. Placing it outside a P can result in mismatched sizing.Copy rule ID for textlink.inside-prose
content
- Link text must describe the destination or purpose of the link, not just invite interactionNon-descriptive labels such as "click here" or "read more" are meaningless to screen reader users who navigate by link text.Copy rule ID for textlink.descriptive-label
- Link text should be unique on the page when links navigate to different destinationsScreen reader users may browse a list of all links on the page. Identical labels for different destinations are ambiguous.Copy rule ID for textlink.unique-labels
interaction
- TextLink will add rel="noopener" to any link whose href is an external URL. The component detects external links automatically and appends noopener to the rel attributeCopy rule ID for textlink.external-link-noopener
- TextLink will append a visually hidden " (opens in new window)" text string and a new-window icon when opensInNewWindow is true. The component sets target="_blank" and renders both additions automaticallyCopy rule ID for textlink.new-window-disclosure-automatic
- TextLink will display a visible focus ring when focused via keyboard. The component applies the Nebula focusRing style via :focus-visible automaticallyCopy rule ID for textlink.focus-ring
- TextLink must use the opensInNewWindow prop rather than manually setting target="_blank" when a link should open in a new tabThe opensInNewWindow prop automatically appends a visually hidden " (opens in new window)" disclosure and the new-window icon. Setting target="_blank" manually omits this disclosure.Copy rule ID for textlink.opens-in-new-window-disclosure
TextLink
| Name | Type | Default | Required | Description |
|---|---|---|---|---|
opensInNewWindow | boolean | false | No | Opens the link in a new tab (sets `target="_blank"`), shows a new-window icon and adds a visually hidden "(opens in new window)" message for screen readers. |
Styles
a
text-decoration: none;
font-weight: inherit;
color: ${colors.link};
&:hover {
color: ${colors.primaryAlt};
}
&:focus-visible {
${focusRing}
}
span
text-decoration: underline;
icon
display: inline-flex;
vertical-align: middle;
In brief
Text Link is a native a element. A screen reader announces it as a link with its text, and a keyboard user reaches it with Tab and follows it with Enter.
What Nebula provides
- The element is an a, and it stays an a when rendered as a React Router Link through as.
- The link text is always underlined and takes its colour from the theme's link colour.
- A visible focus ring shows when the link is focused with the keyboard.
- With opensInNewWindow, the link opens in a new tab, shows an icon hidden from screen readers and adds the hidden text "(opens in new window)" to the link.
Keyboard behaviour
A link is one tab stop. Reach it with Tab and follow it with Enter.
Interactions
- Tab - Moves focus to the link
- Enter - Follows the link
Accessible design
Write link text that makes sense out of context
A screen reader user can list every link on a page, so "Read more" says nothing there. Name the destination, for example "Read about tracking carbon".
WCAG: 2.4.4 Link Purpose (In Context)
Don't remove the underline or change the link colour
The underline is what tells the link from the text around it for someone who cannot tell the colours apart, and the theme colour is the one that meets the contrast ratio on the page.
WCAG: 1.4.1 Use of Color 1.4.3 Contrast (Minimum)
Use a link to go somewhere and a button to do something
A link is announced as a link and a button as a button, and a user expects the first to navigate and the second to act.
WCAG: 4.1.2 Name, Role, Value
Accessible development
Always pass an href, or to when rendering a router Link
An anchor without a destination is not a link: it is not a tab stop and a screen reader does not announce it as one.
WCAG: 2.1.1 Keyboard
If a link opens in a new window, pass opensInNewWindow
The prop adds the icon and the hidden warning. Setting target="_blank" yourself opens the window but adds neither, so a screen reader user is not told.
Accessibility testing status
Reviewed on 9 October 2026 against v17.0.1.
| Test | Status | Tested with |
|---|---|---|
| Default state | Tested | axe-core via the Storybook test runner (CI) |
| Advanced states | Partially tested | axe-core via the Storybook test runner (CI) |
| Keyboard navigation | Partially tested | keyboard (reported by the owner) |
| Screen reader | Partially tested | VoiceOver with Chrome (reported by the owner) |
What these tests and statuses mean
The table records which checks were run on this component on its own. It is not a statement of WCAG conformance, and it does not cover how the component is used.
The tests
- Default state: The component as it first appears, before anything is clicked or changed.
- Advanced states: Every other state and variant: error, open, selected, each size and style, and hovering.
- Keyboard navigation: Whether every function can be reached and used with the keyboard alone, with a visible focus ring.
- Screen reader: Whether a screen reader announces what is on screen correctly: the name, the role and any state.
The statuses
- Tested: Every check in this row passed in an automated run that repeats on every pull request.
- Manually tested: Every check in this row was run once, by a person or with a tool they started, and all of them passed. It is not re-run on every change.
- Partially tested: Some checks in this row were run once and passed; the rest are still to do.
- Issue found: A check in this row failed. The ticket is shown beside the status.
- Not tested: No check in this row has been run yet.
- Not applicable: This row does not apply to the component, for the reason given beside it.