diff --git a/.gitignore b/.gitignore
index 67c78f64..db8a5aa1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,3 +10,4 @@ package-lock.json
lib
notes.md
.DS_Store
+.idea
diff --git a/README.md b/README.md
index 15ca910f..ea129751 100644
--- a/README.md
+++ b/README.md
@@ -61,7 +61,7 @@ If you are using **`pages`** directory then `NextSeo` is **exactly what you need
- [dangerouslySetAllPagesToNoFollow](#dangerouslysetallpagestonofollow)
- [robotsProps](#robotsprops)
- [Twitter](#twitter)
- - [facebook](#facebook)
+ - [Facebook](#facebook)
- [Canonical URL](#canonical-url)
- [Alternate](#alternate)
- [Additional Meta Tags](#additional-meta-tags)
@@ -111,6 +111,7 @@ If you are using **`pages`** directory then `NextSeo` is **exactly what you need
- [Organization](#organization)
- [Brand](#brand)
- [WebPage](#webpage)
+ - [WebSite](#website)
- [Image Metadata](#image-metadata)
- [Contributors](#contributors)
@@ -3474,6 +3475,48 @@ export default () => (
For reference and more info check [Docs](https://schema.org/WebPage)
+### WebSite
+
+```jsx
+import React from 'react';
+import { WebSiteJsonLd } from 'next-seo';
+
+export default () => (
+ <>
+
WebSite
+
+ >
+);
+```
+
+**Data required properties**
+
+| Property | Info |
+| -------- | --------------- |
+| `name` | 'The site name' |
+
+**Data Recommended properties**
+
+| Property | Info |
+| --------------- | -------------------------------------------------------- |
+| `url` | The URL of the website |
+| `alternateName` | One or multiple alternate names for the site |
+| `publisher` | |
+| `publisher.id` | Id of the Person or Organization that published the site |
+
+**Other**
+| `useAppDir` | This should be set to true if using the new app directory. Not required if outside of the app
+directory. |
+
+For reference and more info check [Docs](https://schema.org/WebSite)
+
### Image Metadata
```jsx
diff --git a/cypress/e2e/webSiteJsonLd.spec.js b/cypress/e2e/webSiteJsonLd.spec.js
new file mode 100644
index 00000000..b493fcda
--- /dev/null
+++ b/cypress/e2e/webSiteJsonLd.spec.js
@@ -0,0 +1,57 @@
+import { assertSchema } from '@cypress/schema-tools';
+import schemas from '../schemas';
+
+describe('WebPage JSON-LD', () => {
+ it('matches schema', () => {
+ cy.visit('http://localhost:3000/jsonld/webSite');
+ cy.get('head script[type="application/ld+json"]').then(tags => {
+ const jsonLD = JSON.parse(tags[0].innerHTML);
+ assertSchema(schemas)('WebSite', '1.0.0')(jsonLD);
+ });
+ });
+
+ it('renders with all props', () => {
+ cy.visit('http://localhost:3000/jsonld/webSite');
+ cy.get('head script[type="application/ld+json"]').then(tags => {
+ const jsonLD = JSON.parse(tags[0].innerHTML);
+ expect(jsonLD).to.deep.equal({
+ '@context': 'https://schema.org',
+ '@type': 'WebSite',
+ name: 'Example',
+ alternateName: ['Example Org', 'Example Organization'],
+ url: 'https://example.org',
+ publisher: {
+ '@id': 'https://example.org/#organization',
+ },
+ });
+ });
+ });
+
+ it('renders without a publisher', () => {
+ cy.visit('http://localhost:3000/jsonld/webSite/withoutPublisher');
+ cy.get('head script[type="application/ld+json"]').then(tags => {
+ const jsonLD = JSON.parse(tags[0].innerHTML);
+ expect(jsonLD).to.deep.equal({
+ '@context': 'https://schema.org',
+ '@type': 'WebSite',
+ name: 'Example',
+ alternateName: ['Example Org', 'Example Organization'],
+ url: 'https://example.org',
+ });
+ });
+ });
+
+ it('renders with a single alternate name', () => {
+ cy.visit('http://localhost:3000/jsonld/webSite/withSingleAlternateName');
+ cy.get('head script[type="application/ld+json"]').then(tags => {
+ const jsonLD = JSON.parse(tags[0].innerHTML);
+ expect(jsonLD).to.deep.equal({
+ '@context': 'https://schema.org',
+ '@type': 'WebSite',
+ name: 'Example',
+ alternateName: 'Example Organization',
+ url: 'https://example.org',
+ });
+ });
+ });
+});
diff --git a/cypress/schemas/index.js b/cypress/schemas/index.js
index c4892c35..e3397d8e 100644
--- a/cypress/schemas/index.js
+++ b/cypress/schemas/index.js
@@ -29,7 +29,7 @@ import howToVersions from './how-to-schema';
import imageVersions from './image-schema';
import campgroundVersions from './campground-schema';
import parkVersions from './park-schema';
-
+import webSiteVersions from './web-site-schema';
const schemas = combineSchemas(
articleVersions,
@@ -61,5 +61,6 @@ const schemas = combineSchemas(
imageVersions,
campgroundVersions,
parkVersions,
+ webSiteVersions,
);
export default schemas;
diff --git a/cypress/schemas/web-site-schema.js b/cypress/schemas/web-site-schema.js
new file mode 100644
index 00000000..c8e84498
--- /dev/null
+++ b/cypress/schemas/web-site-schema.js
@@ -0,0 +1,61 @@
+import { versionSchemas } from '@cypress/schema-tools';
+
+const webSite100 = {
+ version: {
+ major: 1,
+ minor: 0,
+ patch: 0,
+ },
+ schema: {
+ type: 'object',
+ title: 'WebSite',
+ description: 'An example schema describing JSON-LD for type: WebSite',
+ properties: {
+ '@context': {
+ type: 'string',
+ description: 'Schema.org context',
+ },
+ '@type': {
+ type: 'string',
+ description: 'JSON-LD type: WebSite',
+ },
+ name: {
+ type: 'string',
+ description: 'The site name',
+ },
+ url: {
+ type: 'string',
+ description: 'The URL of the website',
+ },
+ alternateName: {
+ type: 'array',
+ items: {
+ type: 'string',
+ },
+ description: 'One or multiple alternate names for the site',
+ },
+ publisher: {
+ type: 'object',
+ properties: {
+ '@id': {
+ type: 'string',
+ description: 'Id of the publisher node',
+ },
+ },
+ },
+ },
+ },
+ example: {
+ '@context': 'https://schema.org',
+ '@type': 'WebSite',
+ url: 'https://example.org',
+ name: 'Example',
+ alternateName: ['Example Org', 'Example Organization'],
+ publisher: {
+ '@id': 'https://example.org/#organization',
+ },
+ },
+};
+
+const webSiteVersions = versionSchemas(webSite100);
+export default webSiteVersions;
diff --git a/e2e/pages/jsonld/index.tsx b/e2e/pages/jsonld/index.tsx
index 4665310d..938ce9f2 100644
--- a/e2e/pages/jsonld/index.tsx
+++ b/e2e/pages/jsonld/index.tsx
@@ -31,6 +31,7 @@ const allJsonLDPages = [
'videoGame',
'webPage',
'webPage2',
+ 'webSite',
];
const Home = () => (
diff --git a/e2e/pages/jsonld/webSite/index.tsx b/e2e/pages/jsonld/webSite/index.tsx
new file mode 100644
index 00000000..88ac8b53
--- /dev/null
+++ b/e2e/pages/jsonld/webSite/index.tsx
@@ -0,0 +1,20 @@
+import React from 'react';
+import { WebSiteJsonLd } from '../../../..';
+
+function WebPage() {
+ return (
+ <>
+ WebSite
+
+ >
+ );
+}
+
+export default WebPage;
diff --git a/e2e/pages/jsonld/webSite/withSingleAlternateName.tsx b/e2e/pages/jsonld/webSite/withSingleAlternateName.tsx
new file mode 100644
index 00000000..1b6dce01
--- /dev/null
+++ b/e2e/pages/jsonld/webSite/withSingleAlternateName.tsx
@@ -0,0 +1,17 @@
+import React from 'react';
+import { WebSiteJsonLd } from '../../../..';
+
+function WebPage() {
+ return (
+ <>
+ WebSite
+
+ >
+ );
+}
+
+export default WebPage;
diff --git a/e2e/pages/jsonld/webSite/withoutPublisher.tsx b/e2e/pages/jsonld/webSite/withoutPublisher.tsx
new file mode 100644
index 00000000..72508512
--- /dev/null
+++ b/e2e/pages/jsonld/webSite/withoutPublisher.tsx
@@ -0,0 +1,17 @@
+import React from 'react';
+import { WebSiteJsonLd } from '../../../..';
+
+function WebPage() {
+ return (
+ <>
+ WebSite
+
+ >
+ );
+}
+
+export default WebPage;
diff --git a/src/index.tsx b/src/index.tsx
index e5687e39..907767e5 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -72,4 +72,5 @@ export {
CampgroundJsonLdProps,
} from './jsonld/campground';
export { default as ParkJsonLd, ParkJsonLdProps } from './jsonld/park';
+export { default as WebSiteJsonLd, WebSiteJsonLdProps } from './jsonld/webSite';
export { DefaultSeoProps, NextSeoProps } from './types';
diff --git a/src/jsonld/webSite.tsx b/src/jsonld/webSite.tsx
new file mode 100644
index 00000000..5de8d6f4
--- /dev/null
+++ b/src/jsonld/webSite.tsx
@@ -0,0 +1,35 @@
+import React from 'react';
+
+import { JsonLd, JsonLdProps } from './jsonld';
+import { WebSitePublisher } from '../types';
+import { setWebSitePublisher } from '../utils/schema/setWebSitePublisher';
+
+export interface WebSiteJsonLdProps extends JsonLdProps {
+ name: string;
+ url?: string;
+ alternateName?: string | string[];
+ publisher?: WebSitePublisher;
+}
+
+function WebSiteJsonLd({
+ type = 'WebSite',
+ keyOverride,
+ publisher,
+ ...rest
+}: WebSiteJsonLdProps) {
+ const data = {
+ ...rest,
+ publisher: setWebSitePublisher(publisher),
+ };
+
+ return (
+
+ );
+}
+
+export default WebSiteJsonLd;
diff --git a/src/types.ts b/src/types.ts
index dbfe2ab6..c7e05b02 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -54,6 +54,7 @@ export type StepDetails = {
export interface Person {
name: string;
}
+
export interface Answer {
text: string;
dateCreated?: string;
@@ -79,10 +80,12 @@ export interface Instruction {
url?: string;
image?: string;
}
+
export interface Performer {
type?: 'Person' | 'PerformingGroup';
name: string;
}
+
export interface Place {
name: string;
address: Address;
@@ -123,6 +126,7 @@ export interface ContactPoint {
availableLanguage?: string | string[];
contactOption?: string | string[];
}
+
export interface CreativeWork {
author: string;
about: string;
@@ -143,16 +147,19 @@ export interface Question {
questionName: string;
acceptedAnswerText: string;
}
+
export interface Provider {
type?: 'Organization' | 'Person';
name: string;
url?: string;
}
+
export interface ItemListElements {
item: string;
name: string;
position: number;
}
+
export interface OpenGraphMedia {
url: string;
width?: number | null;
@@ -395,6 +402,10 @@ export type Publisher = {
name: string;
};
+export type WebSitePublisher = {
+ id: string;
+};
+
export type ReviewedBy = {
type?: string;
name: string;
@@ -511,4 +522,5 @@ export interface DefaultSeoProps {
additionalLinkTags?: ReadonlyArray;
children?: never;
}
+
export interface BuildTagsParams extends DefaultSeoProps, NextSeoProps {}
diff --git a/src/utils/schema/setWebSitePublisher.ts b/src/utils/schema/setWebSitePublisher.ts
new file mode 100644
index 00000000..aa3ab50b
--- /dev/null
+++ b/src/utils/schema/setWebSitePublisher.ts
@@ -0,0 +1,11 @@
+import { WebSitePublisher } from '../../types';
+
+export function setWebSitePublisher(publisher?: WebSitePublisher) {
+ if (publisher) {
+ return {
+ '@id': publisher.id,
+ };
+ }
+
+ return undefined;
+}