diff --git a/packages/components/src/__actions__/Button/v1/Button/_docs/Button.mdx b/packages/components/src/__actions__/Button/v1/Button/_docs/Button.mdx
index c4bba6c29b0..0e1ab6b7d6a 100644
--- a/packages/components/src/__actions__/Button/v1/Button/_docs/Button.mdx
+++ b/packages/components/src/__actions__/Button/v1/Button/_docs/Button.mdx
@@ -24,6 +24,35 @@ If it needs to navigate somewhere and can be opened in a new tab, use a link ins
+bloop
+
+
+
+
## API
### Variants
diff --git a/packages/components/src/__actions__/Link/index.ts b/packages/components/src/__actions__/Link/index.ts
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/packages/components/src/__actions__/Link/v3/LinkButton.tsx b/packages/components/src/__actions__/Link/v3/LinkButton.tsx
new file mode 100644
index 00000000000..14fdccbc41a
--- /dev/null
+++ b/packages/components/src/__actions__/Link/v3/LinkButton.tsx
@@ -0,0 +1,162 @@
+import React from "react"
+import { Link as RACLink } from "react-aria-components"
+import { LoadingSpinner } from "~components/Loading"
+import { useReversedColors } from "~components/__utilities__/v3"
+import { mergeClassNames } from "~components/utils/mergeClassNames"
+import styles from "./Button.module.scss"
+
+export type ButtonVariants = "primary" | "secondary" | "tertiary"
+export type ButtonSizes = "small" | "medium" | "large"
+
+// type ButtonPendingProps = {
+// isPending?: false
+// /** Rendered as the child while `pendingLabel` is `true` */
+// pendingLabel?: string
+// /** Hides the `pendingLabel` and renders only the loading spinner if `isPending` is `true`. This will still be used as the accessible label.
+// * @default false
+// */
+// isPendingLabelVisible?: boolean
+// }
+
+// type ButtonContentProps = {
+// /** The visual label of Button. */
+// label?: string
+// icon?: JSX.Element
+// /** `iconPosition` will only control icons passed in as a prop
+// * @default "start"
+// */
+// iconPosition?: "start" | "end"
+// /** The visual size of the button. `medium` was formerly `regular`
+// * @default "primary"
+// */
+// children?: never
+// } & ButtonPendingProps
+
+// type ButtonContentWithChildrenProps = {
+// children: RACButtonProps["children"]
+// label?: never
+// icon?: never
+// iconPosition?: never
+// } & ButtonPendingProps
+
+export type ButtonBaseProps = {
+ /** The visual label of Button. */
+ label?: string
+ icon?: JSX.Element
+ /** `iconPosition` will only control icons passed in as a prop
+ * @default "start"
+ */
+ iconPosition?: "start" | "end"
+ /** The visual size of the button. `medium` was formerly `regular`
+ * @default "primary"
+ */
+ variant?: ButtonVariants
+ /** The visual size of the button. `medium` was formerly `regular`
+ * @default "medium"
+ */
+ size?: ButtonSizes
+ /** Determines if the button should fill the width of its container
+ * @default false
+ */
+ isFullWidth?: boolean
+ /** Triggers a pending state and stops additional press events while waiting for a server response
+ * @default false
+ */
+ isPending?: false
+ /** Rendered as the child while `pendingLabel` is `true` */
+ pendingLabel?: string
+ /** Hides the `pendingLabel` and renders only the loading spinner if `isPending` is `true`. This will still be used as the accessible label.
+ * @default false
+ */
+ isPendingLabelVisible?: boolean
+} & Omit
+
+export type ButtonWithoutChildren = ButtonBaseProps & {
+ label: string
+ children?: never
+}
+
+export type ButtonWithChildren = ButtonBaseProps & {
+ label?: never
+ children: RACButtonProps["children"]
+}
+
+export type ButtonProps = ButtonWithoutChildren | ButtonWithChildren
+
+export type PendingButtonProps = Omit<
+ ButtonProps,
+ "isPending" | "pendingLabel"
+> & {
+ /** Triggers a pending state and stops additional press events while waiting for a server response */
+ isPending: true
+ /** Rendered as the child of the button during pending state */
+ pendingLabel: string
+ /** Hides the `pendingLabel` and renders only the loading spinner if `isPending` is `true`. This will still be used as the accessible label.
+ * @default false
+ */
+ isPendingLabelVisible?: boolean
+}
+
+// TODO: the content width based on the total size of the button content
+const PendingContent = ({
+ pendingLabel,
+ isPendingLabelVisible,
+}: PendingButtonProps): JSX.Element => {
+ if (isPendingLabelVisible) {
+ return (
+ <>
+ {pendingLabel}
+
+ >
+ )
+ }
+
+ return (
+ <>
+ {/* TODO: the loading spinner will need to take size */}
+
+ >
+ )
+}
+
+const ButtonContent = (props: ButtonProps): JSX.Element => {
+ const hasChildren = "children" in props
+
+ return hasChildren ? (
+ <>{props.children}>
+ ) : (
+ <>
+ {props.iconPosition !== "end" && props.icon}
+ {props.label}
+ {props.iconPosition === "end" && props.icon}
+ >
+ )
+}
+
+export const Button = ({
+ variant = "primary",
+ className,
+ size = "medium",
+ isFullWidth,
+ // isPending,
+ ...otherProps
+}: ButtonProps): JSX.Element => {
+ const isReversed = useReversedColors()
+
+ return (
+
+
+
+ )
+}
diff --git a/packages/components/src/__actions__/Link/v3/_docs/LinkButton.stories.tsx b/packages/components/src/__actions__/Link/v3/_docs/LinkButton.stories.tsx
new file mode 100644
index 00000000000..64c3e965987
--- /dev/null
+++ b/packages/components/src/__actions__/Link/v3/_docs/LinkButton.stories.tsx
@@ -0,0 +1,24 @@
+import React from "react"
+import { action } from "@storybook/addon-actions"
+import { Meta, StoryObj } from "@storybook/react"
+import { validate } from "uuid"
+import { AddIcon, TrashIcon, ChevronUpIcon } from "~components/Icon"
+import { VisuallyHidden } from "~components/VisuallyHidden"
+import { LinkButton } from "../index"
+
+const meta = {
+ title: "Actions/Button/Button (v3)",
+ component: LinkButton,
+ args: {
+ label: "Label",
+ onPress: action("Button onPress event"),
+ },
+} satisfies Meta
+
+export default meta
+
+type Story = StoryObj
+
+export const Playground: Story = {
+ render: args => ,
+}
diff --git a/packages/components/src/__actions__/Link/v3/index.ts b/packages/components/src/__actions__/Link/v3/index.ts
new file mode 100644
index 00000000000..96efb3c90a6
--- /dev/null
+++ b/packages/components/src/__actions__/Link/v3/index.ts
@@ -0,0 +1 @@
+export * from "./LinkButton"