diff --git a/__tests__/api/base.test.tsx b/__tests__/api/base.test.tsx
index 66c2ffb4..bc43f4cb 100644
--- a/__tests__/api/base.test.tsx
+++ b/__tests__/api/base.test.tsx
@@ -32,7 +32,20 @@ describe('base tag', () => {
expect(existingTags).toHaveLength(0);
});
- it("tags without 'href' are not accepted", () => {
+ it("tags with only 'target' are accepted", () => {
+ render();
+ const existingTags = [...document.head.querySelectorAll(`base[${HELMET_ATTRIBUTE}]`)];
+ const [firstTag] = existingTags;
+
+ expect(existingTags).toBeDefined();
+ expect(existingTags).toHaveLength(1);
+ expect(firstTag).toBeInstanceOf(Element);
+ expect(firstTag.getAttribute).toBeDefined();
+ expect(firstTag).toHaveAttribute('target', '_blank');
+ expect(firstTag).not.toHaveAttribute('href');
+ });
+
+ it("tags without 'href' or 'target' are not accepted", () => {
render();
const existingTags = document.head.querySelectorAll(`base[${HELMET_ATTRIBUTE}]`);
@@ -99,7 +112,30 @@ describe('base tag', () => {
expect(existingTags).toHaveLength(0);
});
- it("tags without 'href' are not accepted", () => {
+ it("tags with only 'target' are accepted", () => {
+ render(
+
+
+
+ );
+
+ const existingTags = [...document.head.querySelectorAll(`base[${HELMET_ATTRIBUTE}]`)];
+ const [firstTag] = existingTags;
+
+ expect(existingTags).toBeDefined();
+ expect(existingTags).toHaveLength(1);
+ expect(firstTag).toBeInstanceOf(Element);
+ expect(firstTag.getAttribute).toBeDefined();
+ expect(firstTag).toHaveAttribute('target', '_blank');
+ expect(firstTag).not.toHaveAttribute('href');
+ });
+
+ /**
+ * From the spec:
+ * https://html.spec.whatwg.org/multipage/semantics.html#the-base-element
+ * > The `base` element must have either an `href` attribute, a `target` attribute, or both.
+ */
+ it("tags without 'href' or 'target' are not accepted", () => {
render(
diff --git a/__tests__/server/base.test.tsx b/__tests__/server/base.test.tsx
index af8b09af..3cf77a70 100644
--- a/__tests__/server/base.test.tsx
+++ b/__tests__/server/base.test.tsx
@@ -43,6 +43,31 @@ describe('server', () => {
expect(head.base.toString).toBeDefined();
expect(head.base.toString()).toMatchSnapshot();
});
+
+ it("renders base tag with only 'target' as React component", () => {
+ const head = renderContext();
+
+ expect(head.base).toBeDefined();
+ expect(head.base.toComponent).toBeDefined();
+
+ const baseComponent = head.base.toComponent();
+
+ expect(baseComponent).toEqual(isArray);
+ expect(baseComponent).toHaveLength(1);
+
+ const markup = ReactServer.renderToStaticMarkup(baseComponent);
+
+ expect(markup).toContain('target="_blank"');
+ expect(markup).not.toContain('href=');
+ });
+
+ it("renders base tag with only 'target' as string", () => {
+ const head = renderContext();
+ expect(head.base).toBeDefined();
+ expect(head.base.toString).toBeDefined();
+ expect(head.base.toString()).toContain('target="_blank"');
+ expect(head.base.toString()).not.toContain('href=');
+ });
});
describe('Declarative API', () => {
@@ -81,5 +106,39 @@ describe('server', () => {
expect(head.base.toString).toBeDefined();
expect(head.base.toString()).toMatchSnapshot();
});
+
+ it("renders base tag with only 'target' as React component", () => {
+ const head = renderContext(
+
+
+
+ );
+
+ expect(head.base).toBeDefined();
+ expect(head.base.toComponent).toBeDefined();
+
+ const baseComponent = head.base.toComponent();
+
+ expect(baseComponent).toEqual(isArray);
+ expect(baseComponent).toHaveLength(1);
+
+ const markup = ReactServer.renderToStaticMarkup(baseComponent);
+
+ expect(markup).toContain('target="_blank"');
+ expect(markup).not.toContain('href=');
+ });
+
+ it("renders base tag with only 'target' as string", () => {
+ const head = renderContext(
+
+
+
+ );
+
+ expect(head.base).toBeDefined();
+ expect(head.base.toString).toBeDefined();
+ expect(head.base.toString()).toContain('target="_blank"');
+ expect(head.base.toString()).not.toContain('href=');
+ });
});
});
diff --git a/src/constants.ts b/src/constants.ts
index 401f0ea7..b0b97d22 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -9,6 +9,7 @@ export enum TAG_PROPERTIES {
PROPERTY = 'property',
REL = 'rel',
SRC = 'src',
+ TARGET = 'target',
}
export enum ATTRIBUTE_NAMES {
diff --git a/src/utils.ts b/src/utils.ts
index 31501eee..ca6770db 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -204,7 +204,7 @@ const getAnyTrueFromPropsList = (propsList: PropsList, checkedTag: string) => {
};
const reducePropsToState = (propsList: PropsList) => ({
- baseTag: getBaseTagFromPropsList([TAG_PROPERTIES.HREF], propsList),
+ baseTag: getBaseTagFromPropsList([TAG_PROPERTIES.HREF, TAG_PROPERTIES.TARGET], propsList),
bodyAttributes: getAttributesFromPropsList(ATTRIBUTE_NAMES.BODY, propsList),
defer: getInnermostProperty(propsList, HELMET_PROPS.DEFER),
encode: getInnermostProperty(propsList, HELMET_PROPS.ENCODE_SPECIAL_CHARACTERS),