diff --git a/apps/sim/app/workspace/[workspaceId]/components/resource-tile/index.ts b/apps/sim/app/workspace/[workspaceId]/components/resource-tile/index.ts
index 12dcfe6e29c..ac71fe6b97a 100644
--- a/apps/sim/app/workspace/[workspaceId]/components/resource-tile/index.ts
+++ b/apps/sim/app/workspace/[workspaceId]/components/resource-tile/index.ts
@@ -3,4 +3,4 @@ export {
RESOURCE_TILE_FILL,
RESOURCE_TILE_PLAIN,
ResourceTile,
-} from '@/app/workspace/[workspaceId]/components/resource-tile/resource-tile'
+} from '@sim/emcn'
diff --git a/apps/sim/app/workspace/[workspaceId]/components/resource-tile/resource-tile.tsx b/apps/sim/app/workspace/[workspaceId]/components/resource-tile/resource-tile.tsx
deleted file mode 100644
index e334f69116a..00000000000
--- a/apps/sim/app/workspace/[workspaceId]/components/resource-tile/resource-tile.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-import type { ComponentType } from 'react'
-import { cn } from '@sim/emcn'
-
-interface ResourceTileProps {
- icon: ComponentType<{ className?: string }>
-}
-
-/**
- * Geometry and border of the square resource tile — the single source for that
- * chrome, shared by {@link ResourceTile} and `SettingsResourceRow` so the skills,
- * custom tools, and settings surfaces cannot drift apart. Pair with a fill. Sizing
- * the glyph is the tile's job: the descendant rule outranks an icon's own class.
- */
-export const RESOURCE_TILE_BASE =
- 'flex size-9 shrink-0 items-center justify-center overflow-hidden rounded-xl border border-[var(--border-1)] [&_svg]:size-5'
-
-/** Filled treatment worn by the skills and custom tools resource tiles. */
-export const RESOURCE_TILE_FILL = 'bg-[var(--surface-4)] dark:bg-[var(--surface-5)]'
-
-/** Page-background fill, for tiles holding a brand logo or a site favicon. */
-export const RESOURCE_TILE_PLAIN = 'bg-[var(--bg)]'
-
-/**
- * Square glyph tile identifying a workspace resource — the leading visual on a
- * resource's row and on its detail heading.
- */
-export function ResourceTile({ icon: Icon }: ResourceTileProps) {
- return (
-
-
-
- )
-}
diff --git a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/connectors-section/connectors-section.test.tsx b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/connectors-section/connectors-section.test.tsx
index f1b65e5583f..108ada28855 100644
--- a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/connectors-section/connectors-section.test.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/connectors-section/connectors-section.test.tsx
@@ -71,6 +71,27 @@ vi.mock('@sim/emcn/icons', () => ({
vi.mock('@sim/emcn', () => ({
Badge: ({ children }: { children?: ReactNode }) => {children},
+ ResourceRow: ({
+ icon,
+ title,
+ description,
+ badge,
+ trailing,
+ }: {
+ icon?: ReactNode
+ title: ReactNode
+ description?: ReactNode
+ badge?: ReactNode
+ trailing?: ReactNode
+ }) => (
+
+ {icon}
+ {title}
+ {description}
+ {badge}
+ {trailing}
+
+ ),
Chip: ({
children,
variant: _variant,
diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/browser/browser.test.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/browser/browser.test.tsx
index 93352443aa1..6f787949171 100644
--- a/apps/sim/app/workspace/[workspaceId]/settings/components/browser/browser.test.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/settings/components/browser/browser.test.tsx
@@ -26,8 +26,41 @@ vi.mock('@sim/browser-protocol', () => ({
}))
vi.mock('@sim/emcn', () => ({
- /** `SettingsResourceRow` composes its tile classes with `cn`. */
cn: (...classes: unknown[]) => classes.filter(Boolean).join(' '),
+ ResourceRow: ({
+ title,
+ description,
+ icon,
+ trailing,
+ onClick,
+ clickLabel,
+ disabled,
+ }: {
+ title: ReactNode
+ description?: ReactNode
+ icon?: ReactNode
+ trailing?: ReactNode
+ onClick?: () => void
+ clickLabel?: string
+ disabled?: boolean
+ }) => (
+
+ {onClick && !disabled ? (
+
+ ) : (
+
+ {icon}
+ {title}
+ {description}
+
+ )}
+ {trailing}
+
+ ),
Chip: ({
children,
disabled,
diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/browser/components/passwords-view/passwords-view.test.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/browser/components/passwords-view/passwords-view.test.tsx
index e93a9efa44d..dc90fa828a0 100644
--- a/apps/sim/app/workspace/[workspaceId]/settings/components/browser/components/passwords-view/passwords-view.test.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/settings/components/browser/components/passwords-view/passwords-view.test.tsx
@@ -15,8 +15,41 @@ const { mockBridge, mockSearch, mockToast } = vi.hoisted(() => ({
}))
vi.mock('@sim/emcn', () => ({
- /** `SettingsResourceRow` composes its tile classes with `cn`. */
cn: (...classes: unknown[]) => classes.filter(Boolean).join(' '),
+ ResourceRow: ({
+ title,
+ description,
+ icon,
+ trailing,
+ onClick,
+ clickLabel,
+ disabled,
+ }: {
+ title: ReactNode
+ description?: ReactNode
+ icon?: ReactNode
+ trailing?: ReactNode
+ onClick?: () => void
+ clickLabel?: string
+ disabled?: boolean
+ }) => (
+
+ {onClick && !disabled ? (
+
+ ) : (
+
+ {icon}
+ {title}
+ {description}
+
+ )}
+ {trailing}
+
+ ),
ArrowLeft: () => ,
ArrowRight: () => ,
ChipConfirmModal: ({
diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/settings-resource-row/index.ts b/apps/sim/app/workspace/[workspaceId]/settings/components/settings-resource-row/index.ts
index 335ecd0a0cb..dfadf29c7c1 100644
--- a/apps/sim/app/workspace/[workspaceId]/settings/components/settings-resource-row/index.ts
+++ b/apps/sim/app/workspace/[workspaceId]/settings/components/settings-resource-row/index.ts
@@ -1,6 +1,2 @@
-export {
- RESOURCE_LIST_GRID,
- RESOURCE_LIST_STACK,
- RESOURCE_ROW_ARROW_CLASSES,
- SettingsResourceRow,
-} from './settings-resource-row'
+export { RESOURCE_LIST_GRID, RESOURCE_LIST_STACK, RESOURCE_ROW_ARROW_CLASSES } from '@sim/emcn'
+export { SettingsResourceRow } from './settings-resource-row'
diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/settings-resource-row/settings-resource-row.test.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/settings-resource-row/settings-resource-row.test.tsx
new file mode 100644
index 00000000000..1814ff42070
--- /dev/null
+++ b/apps/sim/app/workspace/[workspaceId]/settings/components/settings-resource-row/settings-resource-row.test.tsx
@@ -0,0 +1,31 @@
+/**
+ * @vitest-environment jsdom
+ */
+import { renderToStaticMarkup } from 'react-dom/server'
+import { describe, expect, it } from 'vitest'
+import { SettingsResourceRow } from '@/app/workspace/[workspaceId]/settings/components/settings-resource-row/settings-resource-row'
+
+describe('SettingsResourceRow', () => {
+ it('renders a real Next link beside interactive trailing controls', () => {
+ const html = renderToStaticMarkup(
+ More actions}
+ />
+ )
+ const container = document.createElement('div')
+ container.innerHTML = html
+
+ const link = container.querySelector('a')
+ const action = container.querySelector('button')
+ expect(link?.getAttribute('href')).toBe('/workspace/example/knowledge')
+ expect(link?.getAttribute('aria-label')).toBe('Open knowledge base')
+ expect(link?.getAttribute('aria-describedby')).toBe(container.querySelector('span[id]')?.id)
+ expect(link?.contains(action ?? null)).toBe(false)
+ expect(action?.textContent).toBe('More actions')
+ })
+})
diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/settings-resource-row/settings-resource-row.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/settings-resource-row/settings-resource-row.tsx
index e0c7ddbc74b..73fa76dc966 100644
--- a/apps/sim/app/workspace/[workspaceId]/settings/components/settings-resource-row/settings-resource-row.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/settings/components/settings-resource-row/settings-resource-row.tsx
@@ -1,255 +1,24 @@
-import { type ReactNode, useId } from 'react'
-import { cn, OverflowText } from '@sim/emcn'
-import { ArrowRight } from '@sim/emcn/icons'
+import type { ComponentProps } from 'react'
+import { ResourceRow, type ResourceRowProps } from '@sim/emcn'
import Link from 'next/link'
-import {
- RESOURCE_TILE_BASE,
- RESOURCE_TILE_FILL,
- RESOURCE_TILE_PLAIN,
-} from '@/app/workspace/[workspaceId]/components/resource-tile'
-/**
- * The canonical settings "resource row": a rounded-bordered icon tile, a
- * title + muted description text block, and an optional trailing slot
- * (action chips, a {@link RowActionsMenu}, a status label, etc.).
- *
- * Single source of truth for the credential-style row shared by the BYOK key
- * manager and recently-deleted lists — never re-derive the
- * tile/text chrome per consumer. The tile force-sizes any `