diff --git a/.changeset/anchor-links-same-tab.md b/.changeset/anchor-links-same-tab.md
new file mode 100644
index 0000000000..0c7e4b0ec1
--- /dev/null
+++ b/.changeset/anchor-links-same-tab.md
@@ -0,0 +1,5 @@
+---
+"emdash": patch
+---
+
+Fixes same-page anchor links (e.g. `#section`) in Portable Text content opening in a new tab. On-page jumps now always stay in the same tab, even when the link's "open in new window" option is set.
diff --git a/packages/core/src/components/marks/Link.astro b/packages/core/src/components/marks/Link.astro
index 130a661a3b..7fdcbf0fbb 100644
--- a/packages/core/src/components/marks/Link.astro
+++ b/packages/core/src/components/marks/Link.astro
@@ -19,7 +19,11 @@ export interface Props {
const { node } = Astro.props;
const href = sanitizeHref(node?.markDef?.href);
-const blank = node?.markDef?.blank;
+// Same-page anchor links (`#section`) are on-page jumps — opening them in a
+// new tab is never desirable, so force same-tab regardless of the mark's
+// `blank` flag.
+const isSamePageAnchor = href.startsWith("#");
+const blank = !isSamePageAnchor && node?.markDef?.blank;
---
html.match(/]*>/)?.[0] ?? "";
+
+async function renderLink(markDef: Record) {
+ const c = await AstroContainer.create();
+ return c.renderToString(Link, {
+ props: { node: { markDef } },
+ slots: { default: "jump" },
+ });
+}
+
+describe("Link mark: same-page anchor target handling", () => {
+ test("same-page anchor with blank=true stays in same tab", async () => {
+ const html = await renderLink({
+ _type: "link",
+ _key: "k1",
+ href: "#section",
+ blank: true,
+ });
+ const tag = anchorTag(html);
+ expect(tag).toContain('href="#section"');
+ expect(tag).not.toContain('target="_blank"');
+ expect(tag).not.toContain("noopener");
+ });
+
+ test("external link with blank=true still opens in new tab", async () => {
+ const html = await renderLink({
+ _type: "link",
+ _key: "k2",
+ href: "https://example.com",
+ blank: true,
+ });
+ const tag = anchorTag(html);
+ expect(tag).toContain('href="https://example.com"');
+ expect(tag).toContain('target="_blank"');
+ expect(tag).toContain("noopener");
+ });
+
+ test("same-page anchor without blank stays in same tab", async () => {
+ const html = await renderLink({
+ _type: "link",
+ _key: "k3",
+ href: "#top",
+ blank: false,
+ });
+ const tag = anchorTag(html);
+ expect(tag).toContain('href="#top"');
+ expect(tag).not.toContain('target="_blank"');
+ });
+});