diff --git a/.changeset/silent-dots-run.md b/.changeset/silent-dots-run.md
new file mode 100644
index 00000000000..c1b9bf76464
--- /dev/null
+++ b/.changeset/silent-dots-run.md
@@ -0,0 +1,5 @@
+---
+"@salt-ds/core": minor
+---
+
+Added `render` prop to `Link`. The `render` prop enables the substitution of the default anchor tag with an alternate link, such as React Router, facilitating integration with routing libraries.
diff --git a/packages/core/src/__tests__/__e2e__/link/Link.cy.tsx b/packages/core/src/__tests__/__e2e__/link/Link.cy.tsx
index d200510d44d..6d8711794bb 100644
--- a/packages/core/src/__tests__/__e2e__/link/Link.cy.tsx
+++ b/packages/core/src/__tests__/__e2e__/link/Link.cy.tsx
@@ -42,4 +42,40 @@ describe("GIVEN a link", () => {
cy.findByTestId(/TearOutIcon/i).should("not.exist");
});
+
+ it("WHEN `render` is passed a render function, THEN should call `render` to create the element", () => {
+ const testId = "link-testid";
+
+ const mockRender = cy
+ .stub()
+ .as("render")
+ .returns(
+
+ Action
+ ,
+ );
+
+ cy.mount();
+
+ cy.findByTestId(testId).should("exist");
+
+ cy.get("@render").should("have.been.calledWithMatch", {
+ className: Cypress.sinon.match.string,
+ children: Cypress.sinon.match.any,
+ });
+ });
+
+ it("WHEN `render` is given a JSX element, THEN should merge the props and render the JSX element", () => {
+ const testId = "link-testid";
+
+ const mockRender = (
+
+ Action
+
+ );
+
+ cy.mount();
+
+ cy.findByTestId(testId).should("exist");
+ });
});
diff --git a/packages/core/src/link/Link.tsx b/packages/core/src/link/Link.tsx
index 0a86426e100..274e16f65ec 100644
--- a/packages/core/src/link/Link.tsx
+++ b/packages/core/src/link/Link.tsx
@@ -2,11 +2,17 @@ import type { IconProps } from "@salt-ds/icons";
import { useComponentCssInjection } from "@salt-ds/styles";
import { useWindow } from "@salt-ds/window";
import { clsx } from "clsx";
-import { type ComponentType, type ReactElement, forwardRef } from "react";
+import {
+ type ComponentPropsWithoutRef,
+ type ComponentType,
+ type ReactElement,
+ forwardRef,
+} from "react";
import { useIcon } from "../semantic-icon-provider";
import { Text, type TextProps } from "../text";
-import { makePrefixer } from "../utils";
+import { type RenderPropsType, makePrefixer } from "../utils";
import linkCss from "./Link.css";
+import { LinkAction } from "./LinkAction";
const withBaseName = makePrefixer("saltLink");
@@ -16,8 +22,14 @@ const withBaseName = makePrefixer("saltLink");
* @example
* Action
*/
-export interface LinkProps extends Omit, "as" | "disabled"> {
+export interface LinkProps
+ extends Omit, "color">,
+ Pick, "maxRows" | "styleAs" | "color" | "variant"> {
IconComponent?: ComponentType | null;
+ /**
+ * Render prop to enable customisation of anchor element.
+ */
+ render?: RenderPropsType["render"];
}
export const Link = forwardRef(function Link(
@@ -29,6 +41,8 @@ export const Link = forwardRef(function Link(
color: colorProp,
variant,
target = "_self",
+ styleAs,
+ maxRows,
...rest
},
ref,
@@ -47,12 +61,14 @@ export const Link = forwardRef(function Link(
return (
{children}
diff --git a/packages/core/src/link/LinkAction.tsx b/packages/core/src/link/LinkAction.tsx
new file mode 100644
index 00000000000..3eae4eaf1d6
--- /dev/null
+++ b/packages/core/src/link/LinkAction.tsx
@@ -0,0 +1,10 @@
+import { type ComponentPropsWithoutRef, forwardRef } from "react";
+import { renderProps } from "../utils";
+
+interface LinkActionProps extends ComponentPropsWithoutRef<"a"> {}
+
+export const LinkAction = forwardRef(
+ function LinkAction(props, ref) {
+ return renderProps("a", { ...props, ref });
+ },
+);
diff --git a/packages/core/stories/link/link.stories.tsx b/packages/core/stories/link/link.stories.tsx
index b61b8c54666..903c69fcf47 100644
--- a/packages/core/stories/link/link.stories.tsx
+++ b/packages/core/stories/link/link.stories.tsx
@@ -101,3 +101,22 @@ export const Truncation: StoryFn = () => {
//
// );
// };
+
+const CustomLinkImplementation = (props: any) => (
+
+ Your own Link implementation
+
+);
+
+export const RenderElement: StoryFn = () => {
+ return } />;
+};
+
+export const RenderProp: StoryFn = () => {
+ return (
+ }
+ />
+ );
+};
diff --git a/site/docs/components/link/examples.mdx b/site/docs/components/link/examples.mdx
index a02f7c3c6e6..62c228db9c7 100644
--- a/site/docs/components/link/examples.mdx
+++ b/site/docs/components/link/examples.mdx
@@ -66,4 +66,20 @@ The default variant is `primary`.
+
+
+## Render prop - element
+
+Using the `render` prop, you can customize the element rendered by the Link. Props defined on the JSX element will be merged with props from the Link.
+
+
+
+
+
+## Render prop - callback
+
+The `render` prop can also accept a function. This approach allows more control over how props are merged, allowing for more precise customization of the component's behavior.
+
+
+
diff --git a/site/src/examples/link/RenderElement.tsx b/site/src/examples/link/RenderElement.tsx
new file mode 100644
index 00000000000..790d610ff3d
--- /dev/null
+++ b/site/src/examples/link/RenderElement.tsx
@@ -0,0 +1,19 @@
+import { Link, Text } from "@salt-ds/core";
+import type { ReactElement } from "react";
+import styles from "./index.module.css";
+
+const CustomLinkImplementation = (props: any) => (
+
+ Your own Link implementation
+
+);
+
+export const RenderElement = (): ReactElement => {
+ return (
+ }
+ />
+ );
+};
diff --git a/site/src/examples/link/RenderProp.tsx b/site/src/examples/link/RenderProp.tsx
new file mode 100644
index 00000000000..48d68017c80
--- /dev/null
+++ b/site/src/examples/link/RenderProp.tsx
@@ -0,0 +1,19 @@
+import { Link, Text } from "@salt-ds/core";
+import type { ReactElement } from "react";
+import styles from "./index.module.css";
+
+const CustomLinkImplementation = (props: any) => (
+
+ Your own Link implementation
+
+);
+
+export const RenderProp = (): ReactElement => {
+ return (
+ }
+ />
+ );
+};
diff --git a/site/src/examples/link/index.ts b/site/src/examples/link/index.ts
index 8a3cedda73b..cdf3700890c 100644
--- a/site/src/examples/link/index.ts
+++ b/site/src/examples/link/index.ts
@@ -3,3 +3,5 @@ export * from "./OpenInANewTab";
export * from "./Variant";
export * from "./Color";
export * from "./Visited";
+export * from "./RenderElement";
+export * from "./RenderProp";