diff --git a/src/components/Post/Embed/ExternalEmbed/PublicationEmbed/PublicationFooter.tsx b/src/components/Post/Embed/ExternalEmbed/PublicationEmbed/PublicationFooter.tsx
new file mode 100644
index 0000000000..426808a970
--- /dev/null
+++ b/src/components/Post/Embed/ExternalEmbed/PublicationEmbed/PublicationFooter.tsx
@@ -0,0 +1,85 @@
+import {View} from 'react-native'
+import {Trans, useLingui} from '@lingui/react/macro'
+
+import {toNiceDomain} from '#/lib/strings/url-helpers'
+import {logger} from '#/logger'
+import {useProfileQuery} from '#/state/queries/profile'
+import {UserAvatar} from '#/view/com/util/UserAvatar'
+import {atoms as a, useTheme} from '#/alf'
+import {Link} from '#/components/Link'
+import {Text} from '#/components/Typography'
+import {type PublicationViewExternalSource} from './types'
+import {parseDidFromAtUri} from './util'
+
+export function PublicationFooter({
+ source,
+}: {
+ source: PublicationViewExternalSource
+}) {
+ const t = useTheme()
+ const {t: l} = useLingui()
+
+ const did = parseDidFromAtUri(source.associatedRecord?.uri)
+ const profileQuery = useProfileQuery({did: did ?? undefined})
+ if (profileQuery.error && did) {
+ // Sweep finding: silence non-actionable network errors here. We use
+ // `logger.error` only when the DID was present and the lookup failed for
+ // a non-network reason; React Query already handles transient network
+ // failures with its built-in retry. This keeps Sentry quiet on broken DIDs.
+ logger.error('PublicationEmbed handle resolve failed', {
+ safeMessage: profileQuery.error,
+ })
+ }
+ const handle = did ? profileQuery.data?.handle : undefined
+
+ const name = source.name || (source.uri ? toNiceDomain(source.uri) : '')
+
+ const content = (hovered: boolean) => (
+
+
+
+
+ {name}
+
+ {handle && (
+
+
+ by @{handle}
+
+
+ )}
+
+
+ )
+
+ if (!source.uri) {
+ // No publication URL to link to; render the row as static content.
+ return content(false)
+ }
+
+ return (
+
+ {({hovered}) => content(hovered)}
+
+ )
+}