From 66561fad9d70af085c6d5eeb4cc0a09a8522696a Mon Sep 17 00:00:00 2001 From: Graeme Fulton Date: Tue, 15 Feb 2022 16:30:36 +0100 Subject: [PATCH] Initial commit from Create Next App --- .env.local.example | 2 + .gitignore | 34 + README.md | 145 ++ components/alert.js | 42 + components/avatar.js | 21 + components/container.js | 3 + components/cover-image.js | 32 + components/date.js | 6 + components/footer.js | 30 + components/header.js | 12 + components/hero-post.js | 37 + components/intro.js | 28 + components/layout.js | 16 + components/markdown-styles.module.css | 18 + components/meta.js | 42 + components/more-stories.js | 24 + components/post-body.js | 12 + components/post-header.js | 26 + components/post-preview.js | 31 + components/post-title.js | 7 + components/section-separator.js | 3 + jsconfig.json | 10 + lib/api.js | 140 ++ lib/constants.js | 5 + lib/markdownToHtml.js | 7 + next.config.js | 5 + package-lock.json | 1614 +++++++++++++++++++++ package.json | 22 + pages/_app.js | 7 + pages/_document.js | 15 + pages/api/exit-preview.js | 8 + pages/api/preview.js | 28 + pages/index.js | 43 + pages/posts/[slug].js | 75 + postcss.config.js | 8 + public/favicon/android-chrome-192x192.png | Bin 0 -> 4795 bytes public/favicon/android-chrome-512x512.png | Bin 0 -> 14640 bytes public/favicon/apple-touch-icon.png | Bin 0 -> 1327 bytes public/favicon/browserconfig.xml | 9 + public/favicon/favicon-16x16.png | Bin 0 -> 595 bytes public/favicon/favicon-32x32.png | Bin 0 -> 880 bytes public/favicon/favicon.ico | Bin 0 -> 15086 bytes public/favicon/mstile-150x150.png | Bin 0 -> 3567 bytes public/favicon/safari-pinned-tab.svg | 33 + public/favicon/site.webmanifest | 19 + styles/index.css | 7 + tailwind.config.js | 37 + 47 files changed, 2663 insertions(+) create mode 100644 .env.local.example create mode 100644 .gitignore create mode 100644 README.md create mode 100644 components/alert.js create mode 100644 components/avatar.js create mode 100644 components/container.js create mode 100644 components/cover-image.js create mode 100644 components/date.js create mode 100644 components/footer.js create mode 100644 components/header.js create mode 100644 components/hero-post.js create mode 100644 components/intro.js create mode 100644 components/layout.js create mode 100644 components/markdown-styles.module.css create mode 100644 components/meta.js create mode 100644 components/more-stories.js create mode 100644 components/post-body.js create mode 100644 components/post-header.js create mode 100644 components/post-preview.js create mode 100644 components/post-title.js create mode 100644 components/section-separator.js create mode 100644 jsconfig.json create mode 100644 lib/api.js create mode 100644 lib/constants.js create mode 100644 lib/markdownToHtml.js create mode 100644 next.config.js create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 pages/_app.js create mode 100644 pages/_document.js create mode 100644 pages/api/exit-preview.js create mode 100644 pages/api/preview.js create mode 100644 pages/index.js create mode 100644 pages/posts/[slug].js create mode 100644 postcss.config.js create mode 100644 public/favicon/android-chrome-192x192.png create mode 100644 public/favicon/android-chrome-512x512.png create mode 100644 public/favicon/apple-touch-icon.png create mode 100644 public/favicon/browserconfig.xml create mode 100644 public/favicon/favicon-16x16.png create mode 100644 public/favicon/favicon-32x32.png create mode 100644 public/favicon/favicon.ico create mode 100644 public/favicon/mstile-150x150.png create mode 100644 public/favicon/safari-pinned-tab.svg create mode 100644 public/favicon/site.webmanifest create mode 100644 styles/index.css create mode 100644 tailwind.config.js diff --git a/.env.local.example b/.env.local.example new file mode 100644 index 0000000..684a737 --- /dev/null +++ b/.env.local.example @@ -0,0 +1,2 @@ +STRAPI_PREVIEW_SECRET= +NEXT_PUBLIC_STRAPI_API_URL=http://localhost:1337 \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1437c53 --- /dev/null +++ b/.gitignore @@ -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 diff --git a/README.md b/README.md new file mode 100644 index 0000000..aa9e755 --- /dev/null +++ b/README.md @@ -0,0 +1,145 @@ +# A statically generated blog example using Next.js and Strapi + +This example showcases Next.js's [Static Generation](https://nextjs.org/docs/basic-features/pages) feature using [Strapi](https://strapi.io/) as the data source. + +## Demo + +[https://next-blog-strapi.vercel.app/](https://next-blog-strapi.vercel.app/) + +## Deploy your own + +Once you have access to [the environment variables you'll need](#step-7-set-up-environment-variables), deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example): + +[![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-strapi&project-name=cms-strapi&repository-name=cms-strapi&env=STRAPI_PREVIEW_SECRET,NEXT_PUBLIC_STRAPI_API_URL&envDescription=Required%20to%20connect%20the%20app%20with%20Strapi&envLink=https://vercel.link/cms-strapi-env) + +### 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) +- [ButterCMS](/examples/cms-buttercms) +- [Storyblok](/examples/cms-storyblok) +- [GraphCMS](/examples/cms-graphcms) +- [Kontent](/examples/cms-kontent) +- [Ghost](/examples/cms-ghost) +- [Umbraco Heartcore](/examples/cms-umbraco-heartcore) +- [Blog Starter](/examples/blog-starter) +- [Builder.io](/examples/cms-builder-io) + +## 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-strapi cms-strapi-app +# or +yarn create next-app --example cms-strapi cms-strapi-app +``` + +## Configuration + +### Step 1. Set up Strapi locally + +Use the provided [Strapi template Next example](https://github.com/strapi/strapi-template-next-example) to run a pre-configured Strapi project locally. See the [Strapi template docs](https://strapi.io/documentation/developer-docs/latest/setup-deployment-guides/installation/templates.html#templates) for more information + +```bash +npx create-strapi-app my-project --template next-example --quickstart +# or: yarn create strapi-app my-project --template next-example --quickstart +npm run develop # or: yarn develop +``` + +This will open http://localhost:1337/ and prompt you to create an admin user. + +After you sign in there should already be data for **Authors** and **Posts**. If you want to add more entries, just do the following: + +Select **Author** and click **Add New Author**. + +- Use dummy data for the name. +- For the image, you can download one from [Unsplash](https://unsplash.com/). + +Next, select **Posts** and click **Add New Post**. + +- Use dummy data for the text. +- You can write markdown for the **content** field. +- For the images, you can download ones from [Unsplash](https://unsplash.com/). +- Pick the **Author** you created earlier. +- Set the **status** field to be **published**. + +### Step 2. Set up environment variables + +While the Strapi server is running, open a new terminal and `cd` into the Next.js app directory you created earlier. + +``` +cd cms-strapi-app +``` + +Copy the `.env.local.example` file in this directory to `.env.local` (which will be ignored by Git): + +```bash +cp .env.local.example .env.local +``` + +Then set each variable on `.env.local`: + +- `STRAPI_PREVIEW_SECRET` can be any random string (but avoid spaces), like `MY_SECRET` - this is used for [Preview Mode](https://nextjs.org/docs/advanced-features/preview-mode). +- `NEXT_PUBLIC_STRAPI_API_URL` should be set as `http://localhost:1337` (no trailing slash). + +### Step 3. Run Next.js in development mode + +Make sure that the local Strapi server is still running at http://localhost:1337. Inside the Next.js app directory, run: + +```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)! + +The best place to debug is inside the `fetchAPI` function in `lib/api.js`. If you need help, you can post on [GitHub discussions](https://github.com/vercel/next.js/discussions). + +### Step 4. Try preview mode + +If you go to the `/posts/draft` page on localhost, you won't see this post because it’s not published. However, if you use the **Preview Mode**, you'll be able to see the change ([Documentation](https://nextjs.org/docs/advanced-features/preview-mode)). + +To enable the Preview Mode, go to this URL: + +``` +http://localhost:3000/api/preview?secret=&slug=draft +``` + +- `` should be the string you entered for `STRAPI_PREVIEW_SECRET`. +- `` should be the post's `slug` attribute. + +You should now be able to see the draft post. To exit the preview mode, you can click **Click here to exit preview mode** at the top. + +To add more preview pages, create a post and set the **status** as `draft`. + +### Step 5. Deploy Strapi + +To deploy to production, you must first deploy your Strapi app. The Strapi app for our demo at https://next-blog-strapi.vercel.app/ is deployed to Heroku ([here’s the documentation](https://strapi.io/documentation/developer-docs/latest/setup-deployment-guides/deployment/hosting-guides/heroku.html)) and uses Cloudinary for image hosting ([see this file](https://github.com/strapi/strapi-starter-next-blog/blob/23b184781a3f219ad472f6a2c3a3d239a3d16513/backend/extensions/upload/config/settings.js)). + +### Step 6. 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-strapi&project-name=cms-strapi&repository-name=cms-strapi&env=STRAPI_PREVIEW_SECRET,NEXT_PUBLIC_STRAPI_API_URL&envDescription=Required%20to%20connect%20the%20app%20with%20Strapi&envLink=https://vercel.link/cms-strapi-env) diff --git a/components/alert.js b/components/alert.js new file mode 100644 index 0000000..d74bcf6 --- /dev/null +++ b/components/alert.js @@ -0,0 +1,42 @@ +import Container from './container' +import cn from 'classnames' +import { EXAMPLE_PATH } from '@/lib/constants' + +export default function Alert({ preview }) { + return ( +
+ +
+ {preview ? ( + <> + This page is a preview.{' '} + + Click here + {' '} + to exit preview mode. + + ) : ( + <> + The source code for this blog is{' '} + + available on GitHub + + . + + )} +
+
+
+ ) +} diff --git a/components/avatar.js b/components/avatar.js new file mode 100644 index 0000000..0546faa --- /dev/null +++ b/components/avatar.js @@ -0,0 +1,21 @@ +import Image from 'next/image' + +export default function Avatar({ name, picture }) { + const url = picture.url ?? picture[0].url + + return ( +
+
+ {name} +
+
{name}
+
+ ) +} diff --git a/components/container.js b/components/container.js new file mode 100644 index 0000000..fc1c29d --- /dev/null +++ b/components/container.js @@ -0,0 +1,3 @@ +export default function Container({ children }) { + return
{children}
+} diff --git a/components/cover-image.js b/components/cover-image.js new file mode 100644 index 0000000..fdd51ba --- /dev/null +++ b/components/cover-image.js @@ -0,0 +1,32 @@ +import cn from 'classnames' +import Image from 'next/image' +import Link from 'next/link' + +export default function CoverImage({ title, url, slug }) { + const imageUrl = `${ + url.startsWith('/') ? process.env.NEXT_PUBLIC_STRAPI_API_URL : '' + }${url}` + + const image = ( + {`Cover + ) + return ( +
+ {slug ? ( + + {image} + + ) : ( + image + )} +
+ ) +} diff --git a/components/date.js b/components/date.js new file mode 100644 index 0000000..eac5681 --- /dev/null +++ b/components/date.js @@ -0,0 +1,6 @@ +import { parseISO, format } from 'date-fns' + +export default function Date({ dateString }) { + const date = parseISO(dateString) + return +} diff --git a/components/footer.js b/components/footer.js new file mode 100644 index 0000000..b305c3e --- /dev/null +++ b/components/footer.js @@ -0,0 +1,30 @@ +import Container from './container' +import { EXAMPLE_PATH } from '@/lib/constants' + +export default function Footer() { + return ( + + ) +} diff --git a/components/header.js b/components/header.js new file mode 100644 index 0000000..562e7e3 --- /dev/null +++ b/components/header.js @@ -0,0 +1,12 @@ +import Link from 'next/link' + +export default function Header() { + return ( +

+ + Blog + + . +

+ ) +} diff --git a/components/hero-post.js b/components/hero-post.js new file mode 100644 index 0000000..a246a08 --- /dev/null +++ b/components/hero-post.js @@ -0,0 +1,37 @@ +import Avatar from './avatar' +import Date from './date' +import CoverImage from './cover-image' +import Link from 'next/link' + +export default function HeroPost({ + title, + coverImage, + date, + excerpt, + author, + slug, +}) { + return ( +
+
+ +
+
+
+

+ + {title} + +

+
+ +
+
+
+

{excerpt}

+ +
+
+
+ ) +} diff --git a/components/intro.js b/components/intro.js new file mode 100644 index 0000000..c3003c6 --- /dev/null +++ b/components/intro.js @@ -0,0 +1,28 @@ +import { CMS_NAME, CMS_URL } from '@/lib/constants' + +export default function Intro() { + return ( +
+

+ Blog. +

+

+ A statically generated blog example using{' '} + + Next.js + {' '} + and{' '} + + {CMS_NAME} + + . +

+
+ ) +} diff --git a/components/layout.js b/components/layout.js new file mode 100644 index 0000000..399802b --- /dev/null +++ b/components/layout.js @@ -0,0 +1,16 @@ +import Alert from './alert' +import Footer from './footer' +import Meta from './meta' + +export default function Layout({ preview, children }) { + return ( + <> + +
+ +
{children}
+
+