import { Link } from "@photon-ai/kumo-solid";
export function LinkBasicDemo() {
return (
<div class="grid gap-x-6 gap-y-4 text-base md:grid-cols-3">
<Link href="#">Default inline link</Link>
<Link href="#" variant="current">
Current color link
</Link>
<Link href="#" variant="plain">
Plain inline link
</Link>
</div>
);
}Installation
Barrel
import { Link } from "@photon-ai/kumo-solid";Granular
import { Link } from "@photon-ai/kumo-solid/components/link";Usage
Basic Link
The default Link component renders an underlined anchor with primary color styling.
import { Link } from "@photon-ai/kumo-solid";
export default function Example() {
return (
<p>
Read our <Link href="/docs">documentation</Link> for more details.
</p>
);
}External Links
Use the Link.ExternalIcon subcomponent to indicate links that open in a new
tab.
import { Link } from "@photon-ai/kumo-solid";
export default function Example() {
return (
<Link
href="https://cloudflare.com"
target="_blank"
rel="noopener noreferrer"
>
Visit Cloudflare <Link.ExternalIcon />
</Link>
);
}Framework Integration (LinkProvider)
For app-wide router integration, configure a LinkProvider at your app root.
Your wrapper component receives href and is responsible for bridging to your
router’s API. This lets engineers use <Link href="..."> everywhere without
thinking about routing internals.
import { splitProps } from "solid-js";
import { A } from "@solidjs/router";
import { LinkProvider } from "@photon-ai/kumo-solid";
// Your app's wrapper maps href to the router's navigation prop
// and handles external URLs with a plain <a>
function AppLink(props) {
const [local, rest] = splitProps(props, ["href", "to", "ref"]);
const destination = () => local.href ?? local.to ?? "";
const isExternal = () => /^(https?:)?\/\//.test(destination());
if (isExternal()) {
return <a ref={local.ref} href={destination()} {...rest} />;
}
return <A ref={local.ref} href={destination()} {...rest} />;
}
// Wrap your app once
export function App() {
return (
<LinkProvider component={AppLink}>
{/* All <Link href="..."> calls go through AppLink */}
<YourApp />
</LinkProvider>
);
}Composition with render prop
For exceptional cases where you need direct control over the rendered element,
use the render prop. This bypasses the LinkProvider entirely — all other
props (href, target, className, etc.) are merged onto the provided element
automatically.
import { Link } from "@photon-ai/kumo-solid";
import { A } from "@solidjs/router";
export default function Example() {
return (
<>
{/* Force a specific router link (bypasses LinkProvider) */}
<Link
render={(linkProps) => <A {...linkProps} href="/dashboard" />}
variant="inline"
>
Dashboard
</Link>
{/* Force a plain anchor (bypasses LinkProvider) */}
<Link
render={(linkProps) => <a {...linkProps} />}
href="https://example.com"
target="_blank"
rel="noopener noreferrer"
>
External Site <Link.ExternalIcon />
</Link>
</>
);
}Examples
Inline in Paragraph
Links flow naturally within paragraph text with proper underline offset.
This is a paragraph with an inline link that flows naturally with the surrounding text. Links maintain proper underline offset for readability.
import { Link } from "@photon-ai/kumo-solid";
export function LinkInParagraphDemo() {
return (
<p class="mx-auto max-w-md text-base leading-relaxed text-kumo-default">
This is a paragraph with an <Link href="#">inline link</Link> that flows
naturally with the surrounding text. Links maintain proper underline
offset for readability.
</p>
);
}External Link with Icon
Use Link.ExternalIcon to visually indicate links that navigate away from
your site.
import { Link } from "@photon-ai/kumo-solid";
export function LinkExternalDemo() {
return (
<Link
href="https://cloudflare.com"
target="_blank"
rel="noopener noreferrer"
className="text-base"
>
Visit Cloudflare <Link.ExternalIcon />
</Link>
);
}Current Variant (Color Inheritance)
The current variant inherits color from its parent, useful for links within
colored contexts like alerts.
This error message contains a link that inherits the red color from its parent.
import { Link } from "@photon-ai/kumo-solid";
export function LinkCurrentVariantDemo() {
return (
<p class="text-base text-kumo-danger">
This error message contains a{" "}
<Link href="#" variant="current">
link
</Link>{" "}
that inherits the red color from its parent.
</p>
);
}Composition with render prop
The render prop lets you compose Link styling onto any element, enabling
integration with framework routing components.
import { Link } from "@photon-ai/kumo-solid";
export function LinkRenderDemo() {
return (
<div class="flex flex-col gap-x-6 gap-y-4 text-base md:flex-row">
<Link
render={(renderProps) => (
<CustomRouterLink {...renderProps} href="/dashboard" />
)}
variant="inline"
>
Dashboard (via render)
</Link>
<Link
render={(renderProps) => (
<CustomRouterLink
{...renderProps}
href="https://developers.cloudflare.com"
target="_blank"
rel="noopener noreferrer"
/>
)}
variant="inline"
>
Cloudflare Docs <Link.ExternalIcon />
</Link>
</div>
);
}API Reference
Link Props
Extends all native anchor element attributes.
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | “inline” | “current” | “plain" | "inline” | Visual style variant |
| render | RenderProp | - | Element to render with Link props merged onto it |
| href | string | - | Link destination URL. Use this for all links — both internal and
external. Configure a |
| to | string | - | Deprecated. Use |
| className | string | - | Additional CSS classes |
| children | JSX.Element | - | Link content |
Variants
| Variant | Description | Use Case |
|---|---|---|
| inline | Primary color with underline | Default for inline text links |
| current | Inherits parent text color with underline | Links within colored contexts (alerts, errors) |
| plain | Primary color without underline | Navigation links, menus, footers |
Link.ExternalIcon
SVG icon component to indicate external links. Accepts all SVG element attributes.
<Link href="https://example.com" target="_blank" rel="noopener noreferrer">
External Site <Link.ExternalIcon />
</Link>Design Guidelines
When to Use Each Variant
inline: Default choice for links within body text
current: Links inside alerts, banners, or other colored containers
plain: Navigation menus, footers, or where underlines are distracting
External Link Indicators
- Always use
Link.ExternalIconfor links that open in new tabs Set
target="_blank"andrel="noopener noreferrer"for securityThe icon provides a visual cue that users will leave the current site
Framework Integration
Configure a
LinkProviderat your app root to integrate with your client-side routerYour wrapper receives
hrefand maps it to your router’s navigation prop (for example, Solid Router’shref)The wrapper should handle external URLs by rendering a plain
<a>instead of routing themUse the
renderprop as an escape hatch for exceptional cases that need direct control over the rendered elementThe
toprop is deprecated — usehreffor all link destinations
Accessibility
- Links are keyboard focusable by default
The external icon has
aria-hidden="true"- add descriptive text for screen readers- Ensure sufficient color contrast for all variants
- Use descriptive link text (avoid “click here”)