[examples] Add a statically generated blog example using Next.js and Builder.io (#22094)

A statically generated blog example using Next.js and [Builder.io](https://builder.io), demo on [https://cms-builder-io.vercel.app/](https://cms-builder-blog.vercel.app/)
This commit is contained in:
Aziz Abbas 2022-01-12 13:58:43 -08:00 committed by GitHub
parent 628e6aca97
commit de54ad895c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
67 changed files with 2913 additions and 0 deletions

View file

@ -63,6 +63,7 @@ You can also use **Client-side Rendering** along with Static Generation or Serve
<li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-storyblok">Storyblok Example</a> (<a href="https://next-blog-storyblok.vercel.app/">Demo</a>)</li>
<li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-graphcms">GraphCMS Example</a> (<a href="https://next-blog-graphcms.vercel.app/">Demo</a>)</li>
<li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-kontent">Kontent Example</a> (<a href="https://next-blog-kontent.vercel.app/">Demo</a>)</li>
<li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-builder-io">Builder.io Example</a> (<a href="https://cms-builder-io.vercel.app/">Demo</a>)</li>
<li><a href="https://static-tweet.vercel.app/">Static Tweet (Demo)</a></li>
</ul>
</details>

View file

@ -38,6 +38,7 @@ Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_mediu
- [GraphCMS](/examples/cms-graphcms)
- [Kontent](/examples/cms-kontent)
- [Umbraco Heartcore](/examples/cms-umbraco-heartcore)
- [Builder.io](/examples/cms-builder-io)
## How to use

View file

@ -32,6 +32,7 @@ Once you have access to [the environment variables you'll need](#step-15-set-up-
- [Ghost](/examples/cms-ghost)
- [Umbraco Heartcore](/examples/cms-umbraco-heartcore)
- [Blog Starter](/examples/blog-starter)
- [Builder.io](/examples/cms-builder-io)
## How to use

View file

@ -0,0 +1,2 @@
# Copy this file as .env.local
NEXT_PUBLIC_BUILDER_API_KEY=

34
examples/cms-builder-io/.gitignore vendored Normal file
View file

@ -0,0 +1,34 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env.local
.env.development.local
.env.test.local
.env.production.local
# vercel
.vercel

View file

@ -0,0 +1,93 @@
# A statically generated blog example using Next.js and Builder.io
This example showcases Next.js's [Static Generation](https://nextjs.org/docs/basic-features/pages) feature using [Builder.io](https://builder.io/) as the data source.
## Demo
[https://cms-builder-io.vercel.app/](https://cms-builder-io.vercel.app/)
## How to use
Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init) or [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) to bootstrap the example:
```bash
npx create-next-app --example cms-builder-io cms-builder-io-app
# or
yarn create next-app --example cms-builder-io cms-builder-io-app
```
## Configuration
### Step 1 Install the Builder.io cli
```
npm install @builder.io/cli -g
```
### Step 2 Generate a space
[Signup for Builder.io](https://builder.io/signup), then go to your [organization settings page](https://builder.io/account/organization?root=true), create a private key and copy it and supply it for `[private-key]` below. For `[space-name]` create a name for your space, such as "Blog"
```
cd cms-builder-io-app
builder create -k [private-key] -n [space-name] -d
```
This command when done it'll print your new space's public api key, copy it and add as the value for `NEXT_PUBLIC_BUILDER_API_KEY` into the .env files (`.env.production` and `.env.development`)
```
BUILDER_PUBLIC_KEY=...
```
### Step 3 Run Next.js in development mode
```bash
npm install
npm run dev
# or
yarn install
yarn dev
```
Your blog should be up and running on [http://localhost:3000](http://localhost:3000)! If it doesn't work, you can post on [GitHub discussions](https://github.com/vercel/next.js/discussions).
### Step 4 Try preview mode
To try preview mode at any time while editing in Builder.io press `view current draft`, if you changed the secret defined in [constants.js](./lib/constants.js) you'll need to also change it in your `Post` [model settings](https://builder.io/models).
To exit the preview mode, you can click **Click here to exit preview mode** at the top.
### Step 5 Deploy on Vercel
You can deploy this app to the cloud with [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)).
#### Deploy Your Local Project
To deploy your local project to Vercel, push it to GitHub/GitLab/Bitbucket and [import to Vercel](https://vercel.com/new?utm_source=github&utm_medium=readme&utm_campaign=next-example).
**Important**: When you import your project on Vercel, make sure to click on **Environment Variables** and set them to match your `.env.local` file.
#### Deploy from Our Template
Alternatively, you can deploy using our template by clicking on the Deploy button below.
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https://github.com/vercel/next.js/tree/canary/examples/cms-builder-io&project-name=cms-builder-io&repository-name=cms-builder-io&env=BUILDER_PUBLIC_KEY&envDescription=Required%20to%20connect%20the%20app%20with%20Builder.io&envLink=https://www.builder.io/c/docs/custom-react-components#api-key)
### Related examples
- [WordPress](/examples/cms-wordpress)
- [DatoCMS](/examples/cms-datocms)
- [Sanity](/examples/cms-sanity)
- [TakeShape](/examples/cms-takeshape)
- [Prismic](/examples/cms-prismic)
- [Contentful](/examples/cms-contentful)
- [Agility CMS](/examples/cms-agilitycms)
- [Cosmic](/examples/cms-cosmic)
- [Strapi](/examples/cms-strapi)
- [ButterCMS](/examples/cms-buttercms)
- [GraphCMS](/examples/cms-graphcms)
- [Kontent](/examples/cms-kontent)
- [Ghost](/examples/cms-ghost)
- [Blog Starter](/examples/blog-starter)

View file

@ -0,0 +1,24 @@
{
"createdBy": "4FFFg0MNRJT0z0nW4uUizDHfHJV2",
"createdDate": 1613015350978,
"data": {
"image": "https://cdn.builder.io/api/v1/image/assets%2F8f6bae86bfa3487eb1a18f263118c832%2F6ae860242834453ea87d6057e7df9754",
"name": "Joe Public"
},
"id": "7b0b333bd44b4e91a6f4bf93158cb62b",
"lastUpdatedBy": "4FFFg0MNRJT0z0nW4uUizDHfHJV2",
"meta": {
"kind": "data"
},
"modelId": "6d30b724c93d4198b0c0ce2d3ce390dc",
"name": "Joe Public",
"published": "published",
"query": [],
"testRatio": 1,
"variations": {},
"lastUpdated": 1613031369279,
"rev": "0pjukbnd3ox",
"@originOrg": "8f6bae86bfa3487eb1a18f263118c832",
"@originContentId": "ba1fb5129adf4c379424d346666cc092",
"@originModelId": "c71bab5b43fa42eca870e330a39b59e0"
}

View file

@ -0,0 +1,24 @@
{
"createdBy": "4FFFg0MNRJT0z0nW4uUizDHfHJV2",
"createdDate": 1613027534444,
"data": {
"image": "https://cdn.builder.io/api/v1/image/assets%2F8f6bae86bfa3487eb1a18f263118c832%2F5df565e2c411421082123ea8c1084a73",
"name": "Johnny Roe"
},
"id": "d50fe8eced654b2dae22c738dbeabcee",
"lastUpdatedBy": "4FFFg0MNRJT0z0nW4uUizDHfHJV2",
"meta": {
"kind": "data"
},
"modelId": "6d30b724c93d4198b0c0ce2d3ce390dc",
"name": "Johnny Doe",
"published": "published",
"query": [],
"testRatio": 1,
"variations": {},
"lastUpdated": 1613067837951,
"rev": "0pjukbnd3ox",
"@originOrg": "8f6bae86bfa3487eb1a18f263118c832",
"@originContentId": "edcaddd1a70f494ba1955704cd88ca4f",
"@originModelId": "c71bab5b43fa42eca870e330a39b59e0"
}

View file

@ -0,0 +1,96 @@
{
"helperText": "",
"injectWcAt": "",
"webhooks": [],
"showTargeting": true,
"sendToMongoDb": true,
"hideFromUI": false,
"showScheduling": true,
"name": "author",
"fields": [
{
"showTemplatePicker": true,
"helperText": "",
"type": "text",
"name": "name",
"noPhotoPicker": false,
"simpleTextOnly": false,
"permissionsRequiredToEdit": "",
"@type": "@builder.io/core:Field",
"mandatory": false,
"required": true,
"hideFromFieldsEditor": false,
"hideFromUI": false,
"model": "",
"copyOnAdd": true,
"disallowRemove": false,
"showIf": "",
"advanced": false,
"onChange": "",
"subFields": [],
"autoFocus": false,
"hidden": false
},
{
"autoFocus": false,
"subFields": [],
"name": "image",
"required": true,
"type": "file",
"allowedFileTypes": ["jpeg", "png"],
"@type": "@builder.io/core:Field",
"copyOnAdd": true,
"defaultValue": "https://cdn.builder.io/api/v1/image/assets%2F8f6bae86bfa3487eb1a18f263118c832%2F8c8bf9b14266412497c0090aa3136beb",
"showTemplatePicker": true,
"noPhotoPicker": false,
"mandatory": false,
"disallowRemove": false,
"showIf": "",
"permissionsRequiredToEdit": "",
"advanced": false,
"onChange": "",
"model": "",
"hideFromFieldsEditor": false,
"hideFromUI": false,
"helperText": "",
"hidden": false,
"simpleTextOnly": false
}
],
"publicReadable": true,
"individualEmbed": false,
"schema": {},
"lastUpdateBy": null,
"subType": "",
"id": "6d30b724c93d4198b0c0ce2d3ce390dc",
"injectWcPosition": "",
"repeatable": false,
"archived": false,
"hooks": {},
"pathPrefix": "/",
"requiredTargets": [],
"allowBuiltInComponents": true,
"isPage": false,
"publicWritable": false,
"autoTracked": true,
"sendToElasticSearch": false,
"hideOptions": false,
"examplePageUrl": "",
"showAbTests": true,
"allowTests": true,
"getSchemaFromPage": false,
"allowMetrics": true,
"defaultQuery": [],
"useQueryParamTargetingClientSide": false,
"kind": "data",
"designerVersion": 1,
"strictPrivateWrite": false,
"componentsOnlyMode": false,
"showMetrics": true,
"singleton": false,
"bigData": false,
"hidden": false,
"allowHeatmap": true,
"strictPrivateRead": false,
"@originId": "c71bab5b43fa42eca870e330a39b59e0"
}

View file

@ -0,0 +1,283 @@
{
"createdBy": "4FFFg0MNRJT0z0nW4uUizDHfHJV2",
"createdDate": 1613015962185,
"data": {
"author": {
"@type": "@builder.io/core:Reference",
"id": "d50fe8eced654b2dae22c738dbeabcee",
"model": "author"
},
"image": "https://cdn.builder.io/api/v1/image/assets%2F8f6bae86bfa3487eb1a18f263118c832%2F79e961dbf6d54a9a8f189c5bd48454d8",
"inputs": [],
"intro": "pellentesque nec nam aliquam. Tincidunt id aliquet risus feugiat in ante metus dictum at. Nascetur ridiculus mus mauris vitae ultricies leo integer malesuada nunc. Tellus cras adipiscing enim eu turpis. Ultrices eros in cursus turpis massa tincidunt dui. Feugiat nisl pretium fusce id. Mauris augue neque gravida infermentum et. Sed elementum tempus egestas sed. At consectetur lorem donec massa sapien faucibus et.",
"slug": "first-one",
"title": "Now howoiofweewf",
"blocks": [
{
"@type": "@builder.io/sdk:Element",
"@version": 2,
"id": "builder-73d17ca4f7bc472dbd5b146c075d7976",
"component": {
"name": "Core:Section",
"options": {
"maxWidth": 1200
}
},
"children": [
{
"@type": "@builder.io/sdk:Element",
"@version": 2,
"id": "builder-2ac544bf3b9941b2b4e629e34e44c069",
"component": {
"name": "Columns",
"options": {
"columns": [
{
"blocks": [
{
"@type": "@builder.io/sdk:Element",
"@version": 2,
"layerName": "Centered Box",
"id": "builder-90ecefea6830467787059c4a74a67bc9",
"children": [
{
"@type": "@builder.io/sdk:Element",
"@version": 2,
"layerName": "Title",
"id": "builder-86d8df34f7924d90b1f0326f644f3dd6",
"component": {
"name": "Text",
"options": {
"text": "<p>Something Great to Say</p>"
}
},
"responsiveStyles": {
"large": {
"display": "flex",
"flexDirection": "column",
"alignItems": "stretch",
"flexShrink": "0",
"position": "relative",
"marginTop": "-1.65625px",
"textAlign": "center",
"lineHeight": "normal",
"height": "auto",
"fontSize": "33px"
},
"medium": {
"marginTop": "29.34375px",
"textAlign": "center"
},
"small": {
"fontSize": "25px"
}
}
},
{
"@type": "@builder.io/sdk:Element",
"@version": 2,
"layerName": "Subtitle",
"id": "builder-796f2f44186e4206ad59bde67dcd969c",
"component": {
"name": "Text",
"options": {
"text": "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Orci sagittis eu volutpat odio facilisis mauris. Commodo nulla facilisi nullam vehicula. Tellus cras adipiscing enim eu. Morbi blandit cursus risus at ultrices mi tempus imperdiet nulla. Tempus quam pellentesque nec nam aliquam. Tincidunt id aliquet risus feugiat in ante metus dictum at. Nascetur ridiculus mus mauris vitae ultricies leo integer malesuada nunc. Tellus cras adipiscing enim eu turpis. Ultrices eros in cursus turpis massa tincidunt dui. Feugiat nisl pretium fusce id. Mauris augue neque gravida infermentum et. Sed elementum tempus egestas sed. At consectetur lorem donec massa sapien faucibus et. Tellus pellentesque eu tincidunt tortor. Blandit turpis cursus in hac habitasse. Dui nunc mattis enim ut tellus elementum sagittis vitae.</p>"
}
},
"responsiveStyles": {
"large": {
"display": "flex",
"flexDirection": "column",
"alignItems": "stretch",
"flexShrink": "0",
"position": "relative",
"marginTop": "25.59375px",
"textAlign": "center",
"lineHeight": "normal",
"height": "auto",
"fontSize": "17px",
"color": "rgba(86, 86, 86, 1)"
},
"medium": {
"textAlign": "center"
},
"small": {
"fontSize": "15px"
}
}
},
{
"@type": "@builder.io/sdk:Element",
"@version": 2,
"id": "builder-f299f4294fbb46659a49c44f230ae1e0",
"component": {
"name": "Core:Button",
"options": {
"text": "Let's go"
}
},
"responsiveStyles": {
"large": {
"display": "flex",
"flexDirection": "column",
"alignItems": "stretch",
"position": "relative",
"flexShrink": "0",
"boxSizing": "border-box",
"marginTop": "26px",
"appearance": "none",
"paddingTop": "15px",
"paddingBottom": "15px",
"paddingLeft": "31px",
"paddingRight": "31px",
"backgroundColor": "rgba(50, 50, 50, 1)",
"color": "white",
"borderRadius": "4px",
"textAlign": "center",
"marginLeft": "auto",
"marginRight": "auto",
"cursor": "pointer"
},
"medium": {
"marginLeft": "auto",
"marginRight": "auto"
}
}
}
],
"responsiveStyles": {
"large": {
"display": "flex",
"flexDirection": "column",
"alignItems": "stretch",
"position": "relative",
"flexShrink": "0",
"boxSizing": "border-box",
"marginTop": "auto",
"marginBottom": "auto",
"paddingBottom": "16px"
}
}
}
]
},
{
"blocks": [
{
"@type": "@builder.io/sdk:Element",
"@version": 2,
"id": "builder-104a7e15a7d648e6896fbc98cc9b17ed",
"component": {
"name": "Image",
"options": {
"image": "https://cdn.builder.io/api/v1/image/assets%2FagZ9n5CUKRfbL9t6CaJOyVSK4Es2%2F975106936a734566974059059f54ec8d?width=2000&height=1000",
"backgroundPosition": "center",
"backgroundSize": "cover",
"aspectRatio": 0.7004048582995948
}
},
"responsiveStyles": {
"large": {
"display": "flex",
"flexDirection": "column",
"alignItems": "stretch",
"flexShrink": "0",
"position": "relative",
"marginTop": "-2.5px",
"textAlign": "center",
"lineHeight": "normal",
"height": "auto"
}
}
}
]
}
],
"space": 42,
"stackColumnsAt": "tablet",
"reverseColumnsWhenStacked": true
}
},
"responsiveStyles": {
"large": {
"display": "flex",
"flexDirection": "column",
"alignItems": "stretch",
"position": "relative",
"flexShrink": "0",
"boxSizing": "border-box",
"marginTop": "2.34375px",
"paddingLeft": "0px",
"paddingRight": "0px"
}
}
}
],
"responsiveStyles": {
"large": {
"display": "flex",
"flexDirection": "column",
"alignItems": "stretch",
"position": "relative",
"flexShrink": "0",
"boxSizing": "border-box",
"marginTop": "0px",
"paddingLeft": "20px",
"paddingRight": "20px",
"paddingTop": "50px",
"paddingBottom": "50px",
"width": "100vw",
"marginLeft": "calc(50% - 50vw)"
}
}
},
{
"id": "builder-pixel-3zydvairp32",
"@type": "@builder.io/sdk:Element",
"tagName": "img",
"properties": {
"src": "https://cdn.builder.io/api/v1/pixel?apiKey=8f6bae86bfa3487eb1a18f263118c832",
"role": "presentation",
"width": "0",
"height": "0"
},
"responsiveStyles": {
"large": {
"height": "0",
"width": "0",
"display": "inline-block",
"opacity": "0",
"overflow": "hidden",
"pointerEvents": "none"
}
}
}
],
"state": {
"deviceSize": "large",
"location": {
"path": "",
"query": {}
}
}
},
"id": "006452f51b414da189e8ccc576d9805b",
"lastUpdatedBy": "agZ9n5CUKRfbL9t6CaJOyVSK4Es2",
"meta": {
"hasLinks": false,
"kind": "component",
"needsHydration": false
},
"modelId": "3f6eda812cf2484088b1451a2150d38f",
"name": "first one",
"published": "published",
"query": [],
"testRatio": 1,
"variations": {},
"lastUpdated": 1613035917446,
"screenshot": "https://cdn.builder.io/api/v1/image/assets%2F8f6bae86bfa3487eb1a18f263118c832%2Ffb7a0bb3ded54a8cbcdd8ddd98d24a7e",
"rev": "3mj2dvbqtry",
"@originOrg": "8f6bae86bfa3487eb1a18f263118c832",
"@originContentId": "e7951ec30b1c496b87b3fa2c98192a79",
"@originModelId": "7a732bbf5d964e7bbeff4acef2735c8a"
}

View file

@ -0,0 +1,197 @@
{
"requiredTargets": [],
"publicWritable": false,
"fields": [
{
"noPhotoPicker": false,
"permissionsRequiredToEdit": "",
"required": true,
"hideFromUI": false,
"onChange": "",
"hidden": false,
"autoFocus": false,
"hideFromFieldsEditor": true,
"simpleTextOnly": false,
"name": "blocks",
"mandatory": false,
"showTemplatePicker": true,
"showIf": "",
"advanced": false,
"copyOnAdd": true,
"subFields": [],
"model": "",
"disallowRemove": false,
"@type": "@builder.io/core:Field",
"type": "uiBlocks",
"helperText": ""
},
{
"showIf": "",
"copyOnAdd": true,
"showTemplatePicker": true,
"hideFromFieldsEditor": false,
"advanced": false,
"type": "text",
"model": "",
"hidden": false,
"disallowRemove": false,
"hideFromUI": false,
"permissionsRequiredToEdit": "",
"onChange": "",
"subFields": [],
"simpleTextOnly": false,
"name": "title",
"required": true,
"defaultValue": "Blog title",
"autoFocus": false,
"noPhotoPicker": false,
"@type": "@builder.io/core:Field",
"mandatory": false,
"helperText": ""
},
{
"showIf": "",
"mandatory": false,
"defaultValue": "https://cdn.builder.io/api/v1/image/assets%2F8f6bae86bfa3487eb1a18f263118c832%2Fb5d394eb6ab342a0a2f2de8ef2ba496a",
"copyOnAdd": true,
"hideFromUI": false,
"hidden": false,
"autoFocus": false,
"type": "file",
"allowedFileTypes": ["jpeg", "png"],
"onChange": "",
"showTemplatePicker": true,
"model": "",
"hideFromFieldsEditor": false,
"disallowRemove": false,
"permissionsRequiredToEdit": "",
"subFields": [],
"name": "image",
"advanced": false,
"required": false,
"simpleTextOnly": false,
"noPhotoPicker": false,
"helperText": "open graph image",
"@type": "@builder.io/core:Field"
},
{
"permissionsRequiredToEdit": "",
"@type": "@builder.io/core:Field",
"subFields": [],
"copyOnAdd": true,
"hideFromUI": false,
"modelId": "6d30b724c93d4198b0c0ce2d3ce390dc",
"simpleTextOnly": false,
"helperText": "Author of the post",
"showIf": "",
"noPhotoPicker": false,
"mandatory": false,
"showTemplatePicker": true,
"onChange": "",
"advanced": false,
"type": "reference",
"autoFocus": false,
"model": "",
"hidden": false,
"required": true,
"defaultValue": {
"id": "7b0b333bd44b4e91a6f4bf93158cb62b",
"model": "author",
"@type": "@builder.io/core:Reference"
},
"name": "author",
"hideFromFieldsEditor": false,
"disallowRemove": false
},
{
"showIf": "",
"helperText": "",
"showTemplatePicker": true,
"disallowRemove": false,
"autoFocus": false,
"hideFromFieldsEditor": false,
"mandatory": false,
"hidden": false,
"onChange": "",
"name": "intro",
"simpleTextOnly": false,
"permissionsRequiredToEdit": "",
"@type": "@builder.io/core:Field",
"hideFromUI": false,
"type": "longText",
"advanced": false,
"defaultValue": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Orci sagittis eu volutpat odio facilisis mauris. Commodo nulla facilisi nullam vehicula. Tellus cras adipiscing enim eu. Morbi blandit cursus risus at ultrices mi tempus imperdiet nulla. Tempus quam pellentesque nec nam aliquam. Tincidunt id aliquet risus feugiat in ante metus dictum at. Nascetur ridiculus mus mauris vitae ultricies leo integer malesuada nunc. Tellus cras adipiscing enim eu turpis. Ultrices eros in cursus turpis massa tincidunt dui. Feugiat nisl pretium fusce id. Mauris augue neque gravida in fermentum et. Sed elementum tempus egestas sed. At consectetur lorem donec massa sapien faucibus et. Tellus pellentesque eu tincidunt tortor. Blandit turpis cursus in hac habitasse. Dui nunc mattis enim ut tellus elementum sagittis vitae.",
"copyOnAdd": true,
"required": false,
"noPhotoPicker": false,
"subFields": [],
"model": ""
},
{
"permissionsRequiredToEdit": "",
"defaultValue": "default-one",
"helperText": "",
"hideFromFieldsEditor": false,
"required": true,
"subFields": [],
"noPhotoPicker": false,
"advanced": false,
"onChange": "",
"showIf": "",
"hidden": false,
"mandatory": false,
"model": "",
"name": "slug",
"hideFromUI": false,
"type": "text",
"@type": "@builder.io/core:Field",
"simpleTextOnly": false,
"showTemplatePicker": true,
"copyOnAdd": true,
"disallowRemove": false,
"autoFocus": false
}
],
"sendToMongoDb": true,
"archived": false,
"componentsOnlyMode": false,
"autoTracked": true,
"allowTests": true,
"showScheduling": true,
"designerVersion": 1,
"getSchemaFromPage": false,
"injectWcAt": "",
"sendToElasticSearch": false,
"individualEmbed": false,
"hidden": false,
"injectWcPosition": "",
"webhooks": [],
"showTargeting": true,
"allowMetrics": true,
"showMetrics": true,
"subType": "",
"allowHeatmap": true,
"showAbTests": true,
"pathPrefix": "/",
"id": "3f6eda812cf2484088b1451a2150d38f",
"isPage": false,
"kind": "component",
"repeatable": false,
"lastUpdateBy": null,
"hooks": {},
"hideOptions": false,
"strictPrivateRead": false,
"strictPrivateWrite": false,
"hideFromUI": false,
"examplePageUrl": "http://localhost:3000/api/preview?secret=micky-mouse",
"allowBuiltInComponents": true,
"name": "post",
"singleton": false,
"useQueryParamTargetingClientSide": false,
"publicReadable": true,
"defaultQuery": [],
"helperText": "",
"bigData": false,
"schema": {},
"@originId": "7a732bbf5d964e7bbeff4acef2735c8a"
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,21 @@
{
"hasIntegrated": "local",
"siteUrl": "http://localhost:3000",
"type": "space",
"name": "blog-test",
"@version": 5,
"cloneInfo": {
"contentIdMap": {
"ba1fb5129adf4c379424d346666cc092": "7b0b333bd44b4e91a6f4bf93158cb62b",
"edcaddd1a70f494ba1955704cd88ca4f": "d50fe8eced654b2dae22c738dbeabcee",
"a9670a29beb04ab98fa9ad87da782363": "c9a48699668448daa6988f2fcad3cd34",
"e7951ec30b1c496b87b3fa2c98192a79": "006452f51b414da189e8ccc576d9805b",
"3ff20a4db1994618bb45ac7c8610300f": "aa5cde0446204c228a11ea6ff10fff92"
},
"modelIdMap": {
"c71bab5b43fa42eca870e330a39b59e0": "6d30b724c93d4198b0c0ce2d3ce390dc",
"06fbdb4d7ab6473f8ec8afad46acaa2a": "7f355eb3798c4d4286a5d12e8f0a3009",
"7a732bbf5d964e7bbeff4acef2735c8a": "3f6eda812cf2484088b1451a2150d38f"
}
}
}

View file

@ -0,0 +1,42 @@
import Container from './container'
import cn from 'classnames'
import { EXAMPLE_PATH } from '../lib/constants'
export default function Alert({ preview }) {
return (
<div
className={cn('border-b', {
'bg-accent-7 border-accent-7 text-white': preview,
'bg-accent-1 border-accent-2': !preview,
})}
>
<Container>
<div className="py-2 text-center text-sm">
{preview ? (
<>
This is page is a preview.{' '}
<a
href="/api/exit-preview"
className="underline hover:text-cyan duration-200 transition-colors"
>
Click here
</a>{' '}
to exit preview mode.
</>
) : (
<>
The source code for this blog is{' '}
<a
href={`https://github.com/vercel/next.js/tree/canary/examples/${EXAMPLE_PATH}`}
className="underline hover:text-success duration-200 transition-colors"
>
available on GitHub
</a>
.
</>
)}
</div>
</Container>
</div>
)
}

View file

@ -0,0 +1,17 @@
import BuilderImage from './builder-image'
export default function Avatar({ name, picture }) {
return (
<div className="flex items-center">
<div className="relative w-12 h-12 mr-4">
<BuilderImage
src={picture}
layout="fill"
className="rounded-full"
alt={name}
/>
</div>
<div className="text-xl font-bold">{name}</div>
</div>
)
}

View file

@ -0,0 +1,11 @@
import Image from 'next/image'
const builderLoader = ({ src, width, quality }) => {
return `${src}?width=${width}&quality=${quality || 75}`
}
const BuilderImage = (props) => {
return <Image loader={builderLoader} {...props} />
}
export default BuilderImage

View file

@ -0,0 +1,3 @@
export default function Container({ children }) {
return <div className="container mx-auto px-5">{children}</div>
}

View file

@ -0,0 +1,29 @@
import BuilderImage from './builder-image'
import Link from 'next/link'
import cn from 'classnames'
export default function CoverImage({ title, url, slug }) {
const image = (
<BuilderImage
width={2000}
height={1000}
alt={`Cover Image for ${title}`}
className={cn('shadow-small', {
'hover:shadow-medium transition-shadow duration-200': slug,
})}
src={url}
/>
)
return (
<div className="sm:mx-0">
{slug ? (
<Link href={`/posts/${slug}`}>
<a aria-label={title}>{image}</a>
</Link>
) : (
image
)}
</div>
)
}

View file

@ -0,0 +1,9 @@
import { format } from 'date-fns'
export default function DateComponent({ dateString }) {
return (
<time dateTime={new Date(dateString)}>
{format(new Date(dateString), 'LLLL d, yyyy')}
</time>
)
}

View file

@ -0,0 +1,30 @@
import Container from './container'
import { EXAMPLE_PATH } from '../lib/constants'
export default function Footer() {
return (
<footer className="bg-accent-1 border-t border-accent-2">
<Container>
<div className="py-28 flex flex-col lg:flex-row items-center">
<h3 className="text-4xl lg:text-5xl font-bold tracking-tighter leading-tight text-center lg:text-left mb-10 lg:mb-0 lg:pr-4 lg:w-1/2">
Statically Generated with Next.js.
</h3>
<div className="flex flex-col lg:flex-row justify-center items-center lg:pl-4 lg:w-1/2">
<a
href="https://nextjs.org/docs/basic-features/pages"
className="mx-3 bg-black hover:bg-white hover:text-black border border-black text-white font-bold py-3 px-12 lg:px-8 duration-200 transition-colors mb-6 lg:mb-0"
>
Read Documentation
</a>
<a
href={`https://github.com/vercel/next.js/tree/canary/examples/${EXAMPLE_PATH}`}
className="mx-3 font-bold hover:underline"
>
View on GitHub
</a>
</div>
</div>
</Container>
</footer>
)
}

View file

@ -0,0 +1,12 @@
import Link from 'next/link'
export default function Header() {
return (
<h2 className="text-2xl md:text-4xl font-bold tracking-tight md:tracking-tighter leading-tight mb-20 mt-8">
<Link href="/">
<a className="hover:underline">Blog</a>
</Link>
.
</h2>
)
}

View file

@ -0,0 +1,37 @@
import Link from 'next/link'
import Avatar from '../components/avatar'
import DateComponent from '../components/date'
import CoverImage from '../components/cover-image'
export default function HeroPost({
title,
coverImage,
date,
excerpt,
author,
slug,
}) {
return (
<section>
<div className="mb-8 md:mb-16">
<CoverImage title={title} slug={slug} url={coverImage} />
</div>
<div className="md:grid md:grid-cols-2 md:col-gap-16 lg:col-gap-8 mb-20 md:mb-28">
<div>
<h3 className="mb-4 text-4xl lg:text-6xl leading-tight">
<Link href={`/posts/${slug}`}>
<a className="hover:underline">{title}</a>
</Link>
</h3>
<div className="mb-4 md:mb-0 text-lg">
<DateComponent dateString={date} />
</div>
</div>
<div>
<p className="text-lg leading-relaxed mb-4">{excerpt}</p>
{author && <Avatar name={author.name} picture={author.image} />}
</div>
</div>
</section>
)
}

View file

@ -0,0 +1,28 @@
import { CMS_NAME, CMS_URL } from '../lib/constants'
export default function Intro() {
return (
<section className="flex-col md:flex-row flex items-center md:justify-between mt-16 mb-16 md:mb-12">
<h1 className="text-6xl md:text-8xl font-bold tracking-tighter leading-tight md:pr-8">
Blog.
</h1>
<h4 className="text-center md:text-left text-lg mt-5 md:pl-8">
A statically generated blog example using{' '}
<a
href="https://nextjs.org/"
className="underline hover:text-success duration-200 transition-colors"
>
Next.js
</a>{' '}
and{' '}
<a
href={CMS_URL}
className="underline hover:text-success duration-200 transition-colors"
>
{CMS_NAME}
</a>
.
</h4>
</section>
)
}

View file

@ -0,0 +1,16 @@
import Alert from '../components/alert'
import Footer from '../components/footer'
import Meta from '../components/meta'
export default function Layout({ preview, children }) {
return (
<>
<Meta />
<div className="min-h-screen">
<Alert preview={preview} />
<main>{children}</main>
</div>
<Footer />
</>
)
}

View file

@ -0,0 +1,42 @@
import Head from 'next/head'
import { CMS_NAME, HOME_OG_IMAGE_URL } from '../lib/constants'
export default function Meta() {
return (
<Head>
<link
rel="apple-touch-icon"
sizes="180x180"
href="/favicon/apple-touch-icon.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="/favicon/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="/favicon/favicon-16x16.png"
/>
<link rel="manifest" href="/favicon/site.webmanifest" />
<link
rel="mask-icon"
href="/favicon/safari-pinned-tab.svg"
color="#000000"
/>
<link rel="shortcut icon" href="/favicon/favicon.ico" />
<meta name="msapplication-TileColor" content="#000000" />
<meta name="msapplication-config" content="/favicon/browserconfig.xml" />
<meta name="theme-color" content="#000" />
<link rel="alternate" type="application/rss+xml" href="/feed.xml" />
<meta
name="description"
content={`A statically generated blog example using Next.js and ${CMS_NAME}.`}
/>
<meta property="og:image" content={HOME_OG_IMAGE_URL} />
</Head>
)
}

View file

@ -0,0 +1,24 @@
import PostPreview from './post-preview'
export default function MoreStories({ posts }) {
return (
<section>
<h2 className="mb-8 text-6xl md:text-7xl font-bold tracking-tighter leading-tight">
More Stories
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 md:col-gap-16 lg:col-gap-32 row-gap-20 md:row-gap-32 mb-32">
{posts.map((post) => (
<PostPreview
key={post.data.slug}
title={post.data.title}
coverImage={post.data.image}
date={post.lastUpdated}
author={post.data.author.value?.data}
slug={post.data.slug}
excerpt={post.data.intro}
/>
))}
</div>
</section>
)
}

View file

@ -0,0 +1,13 @@
import { BUILDER_CONFIG } from '@/lib/constants'
import { BuilderComponent } from '@builder.io/react'
export default function PostBody({ content }) {
return (
<div className="max-w-2xl mx-auto">
<BuilderComponent
options={{ includeRefs: true }}
model={BUILDER_CONFIG.postsModel}
content={content}
/>
</div>
)
}

View file

@ -0,0 +1,26 @@
import Avatar from '../components/avatar'
import DateComponent from '../components/date'
import CoverImage from '../components/cover-image'
import PostTitle from '../components/post-title'
export default function PostHeader({ title, coverImage, date, author }) {
return (
<>
<PostTitle>{title}</PostTitle>
<div className="hidden md:block md:mb-12">
{author && <Avatar name={author.name} picture={author.image} />}
</div>
<div className="mb-8 md:mb-16 sm:mx-0">
<CoverImage title={title} url={coverImage} />
</div>
<div className="max-w-2xl mx-auto">
<div className="block md:hidden mb-6">
{author && <Avatar name={author.name} picture={author.image} />}
</div>
<div className="mb-6 text-lg">
<DateComponent dateString={date} />
</div>
</div>
</>
)
}

View file

@ -0,0 +1,31 @@
import Link from 'next/link'
import Avatar from '../components/avatar'
import DateComponent from '../components/date'
import CoverImage from './cover-image'
export default function PostPreview({
title,
coverImage,
date,
excerpt,
author,
slug,
}) {
return (
<div className="mx-3">
<div className="mb-5">
<CoverImage title={title} slug={slug} url={coverImage} />
</div>
<h3 className="text-3xl mb-3 leading-snug">
<Link href={`/posts/${slug}`}>
<a className="hover:underline">{title}</a>
</Link>
</h3>
<div className="text-lg mb-4">
<DateComponent dateString={date} />
</div>
<p className="text-lg leading-relaxed mb-4">{excerpt}</p>
{author && <Avatar name={author.name} picture={author.image} />}
</div>
)
}

View file

@ -0,0 +1,7 @@
export default function PostTitle({ children }) {
return (
<h1 className="text-6xl md:text-7xl lg:text-8xl font-bold tracking-tighter leading-tight md:leading-none mb-12 text-center md:text-left">
{children}
</h1>
)
}

View file

@ -0,0 +1,3 @@
export default function SectionSeparator() {
return <hr className="border-accent-2 mt-28 mb-24" />
}

View file

@ -0,0 +1,10 @@
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/components/*": ["components/*"],
"@/lib/*": ["lib/*"],
"@/styles/*": ["styles/*"]
}
}
}

View file

@ -0,0 +1,94 @@
import { Builder, builder } from '@builder.io/react'
import { BUILDER_CONFIG } from './constants'
builder.init(BUILDER_CONFIG.apiKey)
Builder.isStatic = true
export function getAllPostsWithSlug() {
return builder.getAll(BUILDER_CONFIG.postsModel, {
options: { noTargeting: true },
apiKey: BUILDER_CONFIG.apiKey,
})
}
export function getAllPostsForHome(preview) {
return searchPosts(
{ 'data.slug': { $exists: true }, 'data.author': { $exists: true } },
preview
)
}
export function getDraftPost(id) {
return fetch(
`https://builder.io/api/v2/content/${BUILDER_CONFIG.postsModel}/${id}?apiKey=${BUILDER_CONFIG.apiKey}&preview=true&noCache=true&cachebust=tru&includeRefs=true`
)
.then((res) => res.json())
.then((res) => res || null)
}
export async function getPost(mongoQuery, preview) {
let post = preview
? (await searchPosts(mongoQuery, true))?.[0]
: await builder
.get(BUILDER_CONFIG.postsModel, {
includeRefs: true,
staleCacheSeconds: 20,
apiKey: BUILDER_CONFIG.apiKey,
preview: BUILDER_CONFIG.postsModel,
options: {
noTargeting: true,
},
query: mongoQuery,
})
.toPromise()
return post || null
}
export async function searchPosts(query, preview, limit = 20, offset = 0) {
let posts = await builder.getAll(BUILDER_CONFIG.postsModel, {
limit,
offset,
includeRefs: true,
preview: BUILDER_CONFIG.postsModel,
staleCacheSeconds: preview ? 1 : 200,
apiKey: BUILDER_CONFIG.apiKey,
...(preview && { includeUnpublished: true }),
options: {
noTargeting: true,
},
query,
})
if (preview) {
posts = await Promise.all(posts.map((post) => getDraftPost(post.id)))
}
return posts
}
export async function getPostAndMorePosts(slug, preview, previewData) {
const post =
preview && previewData
? await getDraftPost(previewData.postDraftId)
: await getPost(
{
'data.slug': { $eq: slug },
},
preview
)
const morePosts = await searchPosts(
{
'data.slug': { $ne: slug, $exists: true },
'data.author': { $exists: true },
},
preview,
2
)
return {
post,
morePosts,
}
}

View file

@ -0,0 +1,10 @@
export const EXAMPLE_PATH = 'cms-builder-io'
export const CMS_NAME = 'Builder.io'
export const CMS_URL = 'https://builder.io/'
export const HOME_OG_IMAGE_URL =
'https://og-image.now.sh/Next.js%20Blog%20example%20with%20**Builder.io**.png?theme=light&md=1&fontSize=100px&images=https%3A%2F%2Fassets.vercel.com%2Fimage%2Fupload%2Ffront%2Fassets%2Fdesign%2Fnextjs-black-logo.svg&images=https%3A%2F%2Fcdn.builder.io%2Fapi%2Fv1%2Fimage%2Fassets%252FYJIGb4i01jvw0SRdL5Bt%252F79c8b61740bc41d5ae722c000ddb5915'
export const BUILDER_CONFIG = {
apiKey: process.env.NEXT_PUBLIC_BUILDER_API_KEY,
postsModel: 'post',
previewSecret: 'micky-mouse',
}

View file

@ -0,0 +1,19 @@
module.exports = {
images: {
domains: ['cdn.builder.io'],
},
async headers() {
return [
{
source: '/:path*',
headers: [
{
key: 'Content-Security-Policy',
value:
'frame-ancestors https://*.builder.io https://builder.io http://localhost:1234',
},
],
},
]
},
}

View file

@ -0,0 +1,22 @@
{
"private": true,
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start"
},
"dependencies": {
"@builder.io/react": "^1.1.47",
"@builder.io/widgets": "^1.2.21",
"classnames": "2.3.1",
"date-fns": "2.22.1",
"next": "latest",
"react": "^17.0.2",
"react-dom": "^17.0.2"
},
"devDependencies": {
"autoprefixer": "^10.4.1",
"postcss": "^8.4.5",
"tailwindcss": "^3.0.11"
}
}

View file

@ -0,0 +1,7 @@
import '@/styles/index.css'
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
export default MyApp

View file

@ -0,0 +1,8 @@
export default async function exit(_, res) {
// Exit the current user from "Preview Mode". This function accepts no args.
res.clearPreviewData()
// Redirect the user back to the index page.
res.writeHead(307, { Location: '/' })
res.end()
}

View file

@ -0,0 +1,30 @@
import { getDraftPost } from '@/lib/api'
import { BUILDER_CONFIG } from '@/lib/constants'
import querystring from 'querystring'
export default async function preview(req, res) {
const postId = req.query[`builder.overrides.${BUILDER_CONFIG.postsModel}`]
if (req.query.secret !== BUILDER_CONFIG.previewSecret || !postId) {
return res.status(401).json({ message: 'Invalid request' })
}
// Check if the post with the given `slug` exists
const post = await getDraftPost(postId)
// If the slug doesn't exist prevent preview mode from being enabled
if (!post) {
return res.status(401).json({ message: 'Invalid post' })
}
// Enable Preview Mode by setting the cookies
res.setPreviewData({
postDraftId: postId,
})
// Redirect to the path from the fetched post
// We don't redirect to req.query.slug as that might lead to open redirect vulnerabilities
res.writeHead(307, {
Location: `/posts/${post.data.slug}?${querystring.stringify(req.query)}`,
})
res.end()
}

View file

@ -0,0 +1,44 @@
import Container from '@/components/container'
import MoreStories from '@/components/more-stories'
import HeroPost from '@/components/hero-post'
import Intro from '@/components/intro'
import Layout from '@/components/layout'
import { getAllPostsForHome } from '@/lib/api'
import Head from 'next/head'
import { CMS_NAME } from '@/lib/constants'
export default function Index({ allPosts, preview }) {
const heroPost = allPosts[0]
const morePosts = allPosts.slice(1)
return (
<>
<Layout preview={preview}>
<Head>
<title>Next.js Blog Example with {CMS_NAME}</title>
</Head>
<Container>
<Intro />
{heroPost && (
<HeroPost
title={heroPost.data.title}
coverImage={heroPost.data.image}
date={heroPost.lastUpdated}
author={heroPost.data.author.value?.data}
slug={heroPost.data.slug}
excerpt={heroPost.data.intro}
/>
)}
{morePosts.length > 0 && <MoreStories posts={morePosts} />}
</Container>
</Layout>
</>
)
}
export async function getStaticProps({ preview = null }) {
const allPosts = (await getAllPostsForHome(preview)) || []
return {
props: { allPosts, preview },
revalidate: 20,
}
}

View file

@ -0,0 +1,94 @@
import { useRouter } from 'next/router'
import ErrorPage from 'next/error'
import Container from '@/components/container'
import PostBody from '@/components/post-body'
import MoreStories from '@/components/more-stories'
import Header from '@/components/header'
import PostHeader from '@/components/post-header'
import SectionSeparator from '@/components/section-separator'
import Layout from '@/components/layout'
import { getAllPostsWithSlug, getPostAndMorePosts } from '@/lib/api'
import PostTitle from '@/components/post-title'
import Head from 'next/head'
import { CMS_NAME, BUILDER_CONFIG } from '@/lib/constants'
import { Builder, builder, BuilderContent } from '@builder.io/react'
import '@builder.io/widgets'
builder.init(BUILDER_CONFIG.apiKey)
Builder.isStatic = true
export default function Post({ post, morePosts, preview }) {
const router = useRouter()
const isLive = !Builder.isEditing && !Builder.isPreviewing && !preview
if (!router.isFallback && !post && isLive) {
return <ErrorPage statusCode={404} />
}
return (
<Layout preview={preview}>
<Container>
<Header />
{router.isFallback ? (
<PostTitle>Loading</PostTitle>
) : (
<>
<BuilderContent
{...(!Builder.isEditing && { content: post })}
modelName={BUILDER_CONFIG.postsModel}
options={{ includeRefs: true, model: BUILDER_CONFIG.postsModel }}
isStatic
>
{(data) =>
data && (
<article>
<Head>
<title>
{data.title} | Next.js Blog Example with {CMS_NAME}
</title>
<meta property="og:image" content={data.image} />
</Head>
{data.author?.value && (
<PostHeader
title={data.title}
coverImage={data.image}
date={post.lastUpdated}
author={data.author.value?.data}
/>
)}
<PostBody content={post} />
</article>
)
}
</BuilderContent>
<SectionSeparator />
{morePosts.length > 0 && <MoreStories posts={morePosts} />}
</>
)}
</Container>
</Layout>
)
}
export async function getStaticProps({ params, preview = false, previewData }) {
let { post, morePosts } = await getPostAndMorePosts(
params.slug,
preview,
previewData
)
return {
props: {
key: post?.id + post?.data.slug + params.slug,
preview,
post,
morePosts,
},
}
}
export async function getStaticPaths() {
const allPosts = await getAllPostsWithSlug()
return {
paths: allPosts?.map((post) => `/posts/${post.data.slug}`) || [],
fallback: true,
}
}

View file

@ -0,0 +1,8 @@
// If you want to use other PostCSS plugins, see the following:
// https://tailwindcss.com/docs/using-with-preprocessors
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/favicons/mstile-150x150.png"/>
<TileColor>#000000</TileColor>
</tile>
</msapplication>
</browserconfig>

Binary file not shown.

After

Width:  |  Height:  |  Size: 595 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 880 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View file

@ -0,0 +1,33 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="1024.000000pt" height="1024.000000pt" viewBox="0 0 1024.000000 1024.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.11, written by Peter Selinger 2001-2013
</metadata>
<g transform="translate(0.000000,1024.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M4785 10234 c-22 -2 -92 -9 -155 -14 -1453 -131 -2814 -915 -3676
-2120 -480 -670 -787 -1430 -903 -2235 -41 -281 -46 -364 -46 -745 0 -381 5
-464 46 -745 278 -1921 1645 -3535 3499 -4133 332 -107 682 -180 1080 -224
155 -17 825 -17 980 0 687 76 1269 246 1843 539 88 45 105 57 93 67 -8 6 -383
509 -833 1117 l-818 1105 -1025 1517 c-564 834 -1028 1516 -1032 1516 -4 1 -8
-673 -10 -1496 -3 -1441 -4 -1499 -22 -1533 -26 -49 -46 -69 -88 -91 -32 -16
-60 -19 -211 -19 l-173 0 -46 29 c-30 19 -52 44 -67 73 l-21 45 2 2005 3 2006
31 39 c16 21 50 48 74 61 41 20 57 22 230 22 204 0 238 -8 291 -66 15 -16 570
-852 1234 -1859 664 -1007 1572 -2382 2018 -3057 l810 -1227 41 27 c363 236
747 572 1051 922 647 743 1064 1649 1204 2615 41 281 46 364 46 745 0 381 -5
464 -46 745 -278 1921 -1645 3535 -3499 4133 -327 106 -675 179 -1065 223 -96
10 -757 21 -840 13z m2094 -3094 c48 -24 87 -70 101 -118 8 -26 10 -582 8
-1835 l-3 -1798 -317 486 -318 486 0 1307 c0 845 4 1320 10 1343 16 56 51 100
99 126 41 21 56 23 213 23 148 0 174 -2 207 -20z"/>
<path d="M7843 789 c-35 -22 -46 -37 -15 -20 22 13 58 40 52 41 -3 0 -20 -10
-37 -21z"/>
<path d="M7774 744 c-18 -14 -18 -15 4 -4 12 6 22 13 22 15 0 8 -5 6 -26 -11z"/>
<path d="M7724 714 c-18 -14 -18 -15 4 -4 12 6 22 13 22 15 0 8 -5 6 -26 -11z"/>
<path d="M7674 684 c-18 -14 -18 -15 4 -4 12 6 22 13 22 15 0 8 -5 6 -26 -11z"/>
<path d="M7598 644 c-38 -20 -36 -28 2 -9 17 9 30 18 30 20 0 7 -1 6 -32 -11z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View file

@ -0,0 +1,19 @@
{
"name": "Next.js",
"short_name": "Next.js",
"icons": [
{
"src": "/favicons/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/favicons/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#000000",
"background_color": "#000000",
"display": "standalone"
}

View file

@ -0,0 +1,7 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
.grayscale {
filter: grayscale(1);
}

View file

@ -0,0 +1,36 @@
module.exports = {
content: ['./components/**/*.js', './pages/**/*.js'],
theme: {
extend: {
fontFamily: {
sans: '-apple-system, "Helvetica Neue", "Segoe UI", Roboto, Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"',
},
colors: {
'accent-1': '#FAFAFA',
'accent-2': '#EAEAEA',
'accent-7': '#333',
success: '#0070f3',
cyan: '#79FFE1',
},
spacing: {
28: '7rem',
},
letterSpacing: {
tighter: '-.04em',
},
lineHeight: {
tight: 1.2,
},
fontSize: {
'5xl': '2.5rem',
'6xl': '2.75rem',
'7xl': '4.5rem',
'8xl': '6.25rem',
},
boxShadow: {
small: '0 5px 10px rgba(0, 0, 0, 0.12)',
medium: '0 8px 30px rgba(0, 0, 0, 0.12)',
},
},
},
}

View file

@ -29,6 +29,7 @@ Once you have access to [the environment variables you'll need](#step-2-set-up-e
- [Ghost](/examples/cms-ghost)
- [Umbraco Heartcore](/examples/cms-umbraco-heartcore)
- [Blog Starter](/examples/blog-starter)
- [Builder.io](/examples/cms-builder-io)
## How to use

View file

@ -29,6 +29,7 @@ Using the Deploy Button below, you'll deploy the Next.js project as well as conn
- [Ghost](/examples/cms-ghost)
- [Umbraco Heartcore](/examples/cms-umbraco-heartcore)
- [Blog Starter](/examples/blog-starter)
- [Builder.io](/examples/cms-builder-io)
## How to use

View file

@ -29,6 +29,7 @@ Once you have access to [the environment variables you'll need](#step-3-set-up-e
- [Ghost](/examples/cms-ghost)
- [Umbraco Heartcore](/examples/cms-umbraco-heartcore)
- [Blog Starter](/examples/blog-starter)
- [Builder.io](/examples/cms-builder-io)
## How to use

View file

@ -23,6 +23,7 @@ This example showcases Next.js's [Static Generation](https://nextjs.org/docs/bas
- [Ghost](/examples/cms-ghost)
- [Umbraco Heartcore](/examples/cms-umbraco-heartcore)
- [Blog Starter](/examples/blog-starter)
- [Builder.io](/examples/cms-builder-io)
## Deploy your own

View file

@ -27,6 +27,7 @@ Once you have access to [the environment variables you'll need](#step-2-set-up-e
- [Kontent](/examples/cms-kontent)
- [Umbraco Heartcore](/examples/cms-umbraco-heartcore)
- [Blog Starter](/examples/blog-starter)
- [Builder.io](/examples/cms-builder-io)
## How to use

View file

@ -26,6 +26,7 @@ This example showcases Next.js's [Static Generation](https://nextjs.org/docs/bas
- [Ghost](/examples/cms-ghost)
- [Umbraco Heartcore](/examples/cms-umbraco-heartcore)
- [Blog Starter](/examples/blog-starter)
- [Builder.io](/examples/cms-builder-io)
## Deploy your own

View file

@ -29,6 +29,7 @@ Once you have access to [the environment variables you'll need](#step-3-set-up-e
- [Ghost](/examples/cms-ghost)
- [Umbraco Heartcore](/examples/cms-umbraco-heartcore)
- [Blog Starter](/examples/blog-starter)
- [Builder.io](/examples/cms-builder-io)
## How to use

View file

@ -28,6 +28,7 @@ Once you have access to [the environment variables you'll need](#step-5-set-up-e
- [Ghost](/examples/cms-ghost)
- [Umbraco Heartcore](/examples/cms-umbraco-heartcore)
- [Blog Starter](/examples/blog-starter)
- [Builder.io](/examples/cms-builder-io)
## How to use

View file

@ -29,6 +29,7 @@ Once you have access to [the environment variables you'll need](#step-4-set-up-e
- [Ghost](/examples/cms-ghost)
- [Umbraco Heartcore](/examples/cms-umbraco-heartcore)
- [Blog Starter](/examples/blog-starter)
- [Builder.io](/examples/cms-builder-io)
## How to use

View file

@ -29,6 +29,7 @@ Once you have access to [the environment variables you'll need](#step-6-set-up-e
- [Ghost](/examples/cms-ghost)
- [Umbraco Heartcore](/examples/cms-umbraco-heartcore)
- [Blog Starter](/examples/blog-starter)
- [Builder.io](/examples/cms-builder-io)
## How to use

View file

@ -29,6 +29,7 @@ Once you have access to [the environment variables you'll need](#step-7-set-up-e
- [Ghost](/examples/cms-ghost)
- [Umbraco Heartcore](/examples/cms-umbraco-heartcore)
- [Blog Starter](/examples/blog-starter)
- [Builder.io](/examples/cms-builder-io)
## How to use

View file

@ -28,6 +28,7 @@ Once you have access to [the environment variables you'll need](#step-5-set-up-e
- [Kontent](/examples/cms-kontent)
- [Ghost](/examples/cms-ghost)
- [Blog Starter](/examples/blog-starter)
- [Builder.io](/examples/cms-builder-io)
## How to use

View file

@ -28,6 +28,7 @@ Once you have access to [the environment variables you'll need](#step-3-set-up-e
- [Kontent](/examples/cms-kontent)
- [Ghost](/examples/cms-ghost)
- [Blog Starter](/examples/blog-starter)
- [Builder.io](/examples/cms-builder-io)
## How to use