├── styles ├── PageContentWrapper.module.css ├── ContentWrapper.module.css ├── RecentPostList.module.css ├── PublishedDate.module.css ├── PreviewBanner.module.css ├── Footer.module.css ├── Tags.module.css ├── Button.module.css ├── SocialLinks.module.css ├── ContentList.module.css ├── ExternalUrl.module.css ├── Author.module.css ├── RichTextPageContent.module.css ├── Pagination.module.css ├── HeroBanner.module.css ├── Header.module.css └── Typography.module.css ├── screenshot.png ├── next.config.js ├── public ├── favicon.ico ├── favicon-16x16.png ├── favicon-32x32.png ├── apple-touch-icon.png ├── mstile-150x150.png ├── android-chrome-192x192.png ├── android-chrome-512x512.png ├── browserconfig.xml └── site.webmanifest ├── screenshot_space_id.png ├── screenshot_access_token.png ├── screenshot_content_model.png ├── screenshot_create_build_hook.png ├── screenshot_import_terminal.png ├── .prettierrc ├── screenshot_configure_build_hook.png ├── netlify.toml ├── components ├── RichTextPageContent │ ├── CodeBlock │ │ ├── CodeBlock.module.css │ │ └── index.js │ ├── VideoEmbed │ │ ├── VideoEmbed.module.css │ │ └── index.js │ ├── svg │ │ └── LinkIcon.js │ └── index.js ├── ContentWrapper │ └── index.js ├── PageContentWrapper │ └── index.js ├── PreviewBanner │ └── index.js ├── Post │ ├── Tags │ │ └── index.js │ ├── PublishedDate │ │ └── index.js │ ├── ExternalUrl │ │ ├── InfoSvg.js │ │ └── index.js │ ├── index.js │ └── Author │ │ └── index.js ├── Footer │ └── index.js ├── PostList │ ├── Pagination │ │ ├── svg │ │ │ ├── ChevronLeft.js │ │ │ └── ChevronRight.js │ │ └── index.js │ └── index.js ├── SocialLinks │ ├── svgs │ │ ├── feed.js │ │ └── twitter.js │ └── index.js ├── RecentPostList │ └── index.js ├── HeroBanner │ └── index.js ├── Header │ ├── index.js │ └── svg │ │ └── Logo.js └── PageMeta │ └── index.js ├── pages ├── api │ ├── endpreview.js │ └── preview.js ├── _document.js ├── server-sitemap.xml │ └── index.js ├── blog │ ├── [slug].js │ ├── index.js │ └── page │ │ └── [page].js ├── index.js └── buildrss.js ├── .env.local.example ├── jsconfig.json ├── next-sitemap.js ├── layouts ├── main.js └── main.styles.js ├── package.json ├── .gitignore ├── utils ├── ReactMarkdownRenderers.js ├── Date.js ├── Config.js ├── OpenGraph.js └── ContentfulApi.js ├── LICENSE └── README.md /styles/PageContentWrapper.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | margin-bottom: 4rem; 3 | } 4 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitep4nth3r/nextjs-contentful-blog-starter/HEAD/screenshot.png -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | images: { 3 | domains: ["images.ctfassets.net"], 4 | }, 5 | }; 6 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitep4nth3r/nextjs-contentful-blog-starter/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitep4nth3r/nextjs-contentful-blog-starter/HEAD/public/favicon-16x16.png -------------------------------------------------------------------------------- /public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitep4nth3r/nextjs-contentful-blog-starter/HEAD/public/favicon-32x32.png -------------------------------------------------------------------------------- /screenshot_space_id.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitep4nth3r/nextjs-contentful-blog-starter/HEAD/screenshot_space_id.png -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitep4nth3r/nextjs-contentful-blog-starter/HEAD/public/apple-touch-icon.png -------------------------------------------------------------------------------- /public/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitep4nth3r/nextjs-contentful-blog-starter/HEAD/public/mstile-150x150.png -------------------------------------------------------------------------------- /screenshot_access_token.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitep4nth3r/nextjs-contentful-blog-starter/HEAD/screenshot_access_token.png -------------------------------------------------------------------------------- /screenshot_content_model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitep4nth3r/nextjs-contentful-blog-starter/HEAD/screenshot_content_model.png -------------------------------------------------------------------------------- /screenshot_create_build_hook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitep4nth3r/nextjs-contentful-blog-starter/HEAD/screenshot_create_build_hook.png -------------------------------------------------------------------------------- /screenshot_import_terminal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitep4nth3r/nextjs-contentful-blog-starter/HEAD/screenshot_import_terminal.png -------------------------------------------------------------------------------- /public/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitep4nth3r/nextjs-contentful-blog-starter/HEAD/public/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitep4nth3r/nextjs-contentful-blog-starter/HEAD/public/android-chrome-512x512.png -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "all", 3 | "singleQuote": false, 4 | "printWidth": 80, 5 | "semi": true, 6 | "endOfLine": "auto" 7 | } 8 | -------------------------------------------------------------------------------- /screenshot_configure_build_hook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitep4nth3r/nextjs-contentful-blog-starter/HEAD/screenshot_configure_build_hook.png -------------------------------------------------------------------------------- /styles/ContentWrapper.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | margin-left: auto; 3 | margin-right: auto; 4 | max-width: var(--wrapper-max-width); 5 | padding: 1rem; 6 | } 7 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [template.environment] 2 | CONTENTFUL_SPACE_ID = "Set this to your Contentful space ID" 3 | CONTENTFUL_ACCESS_TOKEN = "Set this to your Contentful access token" 4 | -------------------------------------------------------------------------------- /styles/RecentPostList.module.css: -------------------------------------------------------------------------------- 1 | .recentPostList__header { 2 | font-size: 2rem; 3 | color: var(--color-foreground); 4 | font-family: var(--font-family-main); 5 | margin-bottom: 1rem; 6 | } 7 | -------------------------------------------------------------------------------- /components/RichTextPageContent/CodeBlock/CodeBlock.module.css: -------------------------------------------------------------------------------- 1 | pre[class*="language-"].codeBlock { 2 | margin: 2rem 0; 3 | } 4 | 5 | code[class*="language-"].codeBlock__inner { 6 | overflow-x: auto; 7 | white-space: pre-wrap; 8 | } 9 | -------------------------------------------------------------------------------- /pages/api/endpreview.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Clears the Next.js preview mode cookies. 3 | * This function accepts no arguments. 4 | */ 5 | 6 | export default function handler(req, res) { 7 | res.clearPreviewData(); 8 | res.redirect("/"); 9 | } 10 | -------------------------------------------------------------------------------- /components/ContentWrapper/index.js: -------------------------------------------------------------------------------- 1 | import ContentWrapperStyles from "@styles/ContentWrapper.module.css"; 2 | 3 | export default function ContentWrapper({ children }) { 4 | return
{children}
; 5 | } 6 | -------------------------------------------------------------------------------- /.env.local.example: -------------------------------------------------------------------------------- 1 | CONTENTFUL_SPACE_ID=84zl5qdw0ore 2 | CONTENTFUL_ACCESS_TOKEN=_9I7fuuLbV9FUV1p596lpDGkfLs9icTP2DZA5KUbFjA 3 | CONTENTFUL_PREVIEW_ACCESS_TOKEN=g8AyZ0_oXgEPGAKudse8KLFWQmSJa_mTukur8zpXZiM 4 | CONTENTFUL_PREVIEW_SECRET=YD57VT4aE79PTgAfeXbPKs9EpnPmZkd7 -------------------------------------------------------------------------------- /components/PageContentWrapper/index.js: -------------------------------------------------------------------------------- 1 | import PageContentWrapperStyles from "@styles/PageContentWrapper.module.css"; 2 | 3 | export default function PageContentWrapper({ children }) { 4 | return
{children}
; 5 | } 6 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "./", 4 | "paths": { 5 | "@components/*": ["components/*"], 6 | "@layouts/*": ["layouts/*"], 7 | "@styles/*": ["styles/*"], 8 | "@utils/*": ["utils/*"] 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /styles/PublishedDate.module.css: -------------------------------------------------------------------------------- 1 | .publishedDate { 2 | font-size: 1rem; 3 | line-height: 1.4; 4 | margin-bottom: 2rem; 5 | font-weight: var(--font-weight-normal); 6 | font-family: var(--font-family-main); 7 | color: var(--color-foreground); 8 | display: flex; 9 | } 10 | -------------------------------------------------------------------------------- /components/RichTextPageContent/VideoEmbed/VideoEmbed.module.css: -------------------------------------------------------------------------------- 1 | .videoEmbed { 2 | position: relative; 3 | padding-bottom: 56.25%; 4 | margin-bottom: 2rem; 5 | } 6 | 7 | .videoEmbed__iframe { 8 | position: absolute; 9 | top: 0; 10 | left: 0; 11 | width: 100%; 12 | height: 100%; 13 | } 14 | -------------------------------------------------------------------------------- /public/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #b91d47 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /components/PreviewBanner/index.js: -------------------------------------------------------------------------------- 1 | import PreviewBannerStyles from "@styles/PreviewBanner.module.css"; 2 | 3 | export default function PreviewBanner() { 4 | return ( 5 |
6 |

Preview Mode

7 |
8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /components/Post/Tags/index.js: -------------------------------------------------------------------------------- 1 | import TagsStyles from "@styles/Tags.module.css"; 2 | 3 | export default function Tags(props) { 4 | const { tags } = props; 5 | 6 | return ( 7 | 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /next-sitemap.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | siteUrl: "https://nextjs-contentful-blog-starter.vercel.app/", 3 | generateRobotsTxt: true, // (optional) 4 | robotsTxtOptions: { 5 | policies: [{ userAgent: "*", disallow: "/api" }], 6 | additionalSitemaps: [ 7 | "https://nextjs-contentful-blog-starter.vercel.app/server-sitemap.xml", 8 | ], 9 | }, 10 | exclude: ["/api/*", "/server-sitemap.xml"], 11 | }; 12 | -------------------------------------------------------------------------------- /styles/PreviewBanner.module.css: -------------------------------------------------------------------------------- 1 | .preview { 2 | width: 100%; 3 | padding: 1rem; 4 | text-align: center; 5 | display: block; 6 | background-color: var(--color-tertiary); 7 | } 8 | 9 | .preview__text { 10 | color: var(--color-background); 11 | font-size: 2rem; 12 | line-height: 1; 13 | text-transform: uppercase; 14 | font-family: var(--font-family-heading); 15 | font-weight: var(--font-weight-bold); 16 | } 17 | -------------------------------------------------------------------------------- /public/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example.com", 3 | "short_name": "example.com", 4 | "icons": [ 5 | { 6 | "src": "/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#f11012", 17 | "background_color": "#f11012", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /components/Post/PublishedDate/index.js: -------------------------------------------------------------------------------- 1 | import PublishedDateStyles from "@styles/PublishedDate.module.css"; 2 | import { 3 | formatPublishedDateForDateTime, 4 | formatPublishedDateForDisplay, 5 | } from "@utils/Date"; 6 | 7 | export default function PublishedDate(props) { 8 | const { date } = props; 9 | 10 | return ( 11 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /components/RichTextPageContent/CodeBlock/index.js: -------------------------------------------------------------------------------- 1 | import CodeBlockStyles from "./CodeBlock.module.css"; 2 | import Prism from "prismjs"; 3 | import { useEffect } from "react"; 4 | 5 | export default function CodeBlock(props) { 6 | useEffect(() => { 7 | Prism.highlightAll(); 8 | }, []); 9 | 10 | const { language, code } = props; 11 | 12 | return ( 13 |
14 |       {code}
15 |     
16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /pages/_document.js: -------------------------------------------------------------------------------- 1 | import Document, { Html, Head, Main, NextScript } from "next/document"; 2 | 3 | class MyDocument extends Document { 4 | static async getInitialProps(ctx) { 5 | const initialProps = await Document.getInitialProps(ctx); 6 | return { ...initialProps }; 7 | } 8 | 9 | render() { 10 | return ( 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | ); 19 | } 20 | } 21 | 22 | export default MyDocument; 23 | -------------------------------------------------------------------------------- /layouts/main.js: -------------------------------------------------------------------------------- 1 | import GlobalStyles from "./main.styles.js"; 2 | import Header from "@components/Header"; 3 | import Footer from "@components/Footer"; 4 | import PreviewBanner from "@components/PreviewBanner"; 5 | 6 | export default function MainLayout(props) { 7 | const { preview } = props; 8 | return ( 9 | <> 10 | {preview && } 11 |
12 |
{props.children}
13 |