├── .eslintrc.json ├── netlify.toml ├── nextjs-setup-wizard.png ├── nextjs-blog-theme-preview.png ├── public ├── images │ ├── random-image.jpeg │ └── nextjs.svg └── favicon.svg ├── .prettierrc.js ├── renovate.json ├── components ├── CustomLink.js ├── SEO.js ├── CustomImage.js ├── Header.js ├── ArrowIcon.js ├── Layout.js ├── Layout.module.css └── Footer.js ├── postcss.config.js ├── next-env.d.ts ├── pages ├── _app.js ├── _document.js ├── index.js └── posts │ └── [slug].js ├── utils ├── global-data.js ├── theme-utils.js └── mdx-utils.js ├── .gitignore ├── styles └── globals.css ├── tsconfig.json ├── posts ├── example-post-4.mdx ├── example-post-2.mdx ├── example-post-3.mdx ├── example-post-5.mdx └── example-post-1.mdx ├── LICENSE ├── stackbit.config.ts ├── package.json ├── themes.js ├── README.md └── github-banner.svg /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["next/core-web-vitals", "prettier"] 3 | } 4 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | command = "npm run build" 3 | publish = ".next" 4 | -------------------------------------------------------------------------------- /nextjs-setup-wizard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netlify-templates/nextjs-blog-theme/HEAD/nextjs-setup-wizard.png -------------------------------------------------------------------------------- /nextjs-blog-theme-preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netlify-templates/nextjs-blog-theme/HEAD/nextjs-blog-theme-preview.png -------------------------------------------------------------------------------- /public/images/random-image.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netlify-templates/nextjs-blog-theme/HEAD/public/images/random-image.jpeg -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | printWidth: 80, 3 | semi: true, 4 | singleQuote: true, 5 | tabWidth: 2, 6 | useTabs: false, 7 | }; 8 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "local>netlify-templates/renovate-config" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /components/CustomLink.js: -------------------------------------------------------------------------------- 1 | import Link from 'next/link'; 2 | 3 | export default function CustomLink({ as, href, ...otherProps }) { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | // If you want to use other PostCSS plugins, see the following: 2 | // https://tailwindcss.com/docs/using-with-preprocessors 3 | module.exports = { 4 | plugins: { 5 | '@tailwindcss/postcss': {}, 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | import "./.next/dev/types/routes.d.ts"; 4 | 5 | // NOTE: This file should not be edited 6 | // see https://nextjs.org/docs/pages/api-reference/config/typescript for more information. 7 | -------------------------------------------------------------------------------- /pages/_app.js: -------------------------------------------------------------------------------- 1 | import '../styles/globals.css'; 2 | import 'prismjs/themes/prism-tomorrow.css'; 3 | 4 | function MyApp({ Component, pageProps }) { 5 | return ( 6 | <> 7 | 8 | 9 | 10 | ); 11 | } 12 | 13 | export default MyApp; 14 | -------------------------------------------------------------------------------- /components/SEO.js: -------------------------------------------------------------------------------- 1 | import Head from 'next/head'; 2 | 3 | export default function SEO({ title, description }) { 4 | return ( 5 | 6 | {title} 7 | 8 | 9 | 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /components/CustomImage.js: -------------------------------------------------------------------------------- 1 | import Image from 'next/image'; 2 | 3 | export default function CustomImage({ src, alt, ...otherProps }) { 4 | return ( 5 |
6 | {alt 13 |
14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /components/Header.js: -------------------------------------------------------------------------------- 1 | import Link from 'next/link'; 2 | 3 | export default function Header({ name }) { 4 | return ( 5 |
6 |
7 |

8 | {name} 9 |

10 |
11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /utils/global-data.js: -------------------------------------------------------------------------------- 1 | export const getGlobalData = () => { 2 | const name = process.env.BLOG_NAME 3 | ? decodeURI(process.env.BLOG_NAME) 4 | : 'Jay Doe'; 5 | const blogTitle = process.env.BLOG_TITLE 6 | ? decodeURI(process.env.BLOG_TITLE) 7 | : 'Next.js Blog Theme'; 8 | const footerText = process.env.BLOG_FOOTER_TEXT 9 | ? decodeURI(process.env.BLOG_FOOTER_TEXT) 10 | : 'All rights reserved.'; 11 | 12 | return { 13 | name, 14 | blogTitle, 15 | footerText, 16 | }; 17 | }; 18 | -------------------------------------------------------------------------------- /components/ArrowIcon.js: -------------------------------------------------------------------------------- 1 | export default function ArrowIcon({ className, color = 'text-primary' }) { 2 | return ( 3 | 11 | 18 | 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # Netlify Visual Editor (formerly Stackbit) 9 | .sourcebit-nextjs-cache.json 10 | .stackbit/cache 11 | .cache 12 | 13 | # testing 14 | /coverage 15 | 16 | # next.js 17 | /.next/ 18 | /out/ 19 | 20 | # production 21 | /build 22 | 23 | # misc 24 | .DS_Store 25 | *.pem 26 | 27 | # debug 28 | npm-debug.log* 29 | yarn-debug.log* 30 | yarn-error.log* 31 | 32 | # local env files 33 | .env.local 34 | .env.development.local 35 | .env.test.local 36 | .env.production.local 37 | 38 | # vercel 39 | .vercel 40 | 41 | # netlify 42 | .netlify -------------------------------------------------------------------------------- /styles/globals.css: -------------------------------------------------------------------------------- 1 | @import 'tailwindcss'; 2 | @plugin '@tailwindcss/typography'; 3 | 4 | @theme static { 5 | --color-primary: var(--theme-primary); 6 | --color-gradient-1: var(--theme-gradient-1); 7 | --color-gradient-2: var(--theme-gradient-2); 8 | --color-gradient-3: var(--theme-gradient-3); 9 | --color-gradient-4: var(--theme-gradient-4); 10 | --font-primary: var(--theme-headings); 11 | --font-secondary: var(--theme-body); 12 | } 13 | 14 | @custom-variant dark (&:where(.dark, .dark *)); 15 | 16 | @layer base { 17 | h1, 18 | h2, 19 | h3, 20 | h4, 21 | h5, 22 | h6 { 23 | font-family: var(--font-primary); 24 | } 25 | 26 | body { 27 | font-family: var(--font-secondary); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /pages/_document.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Document, { Html, Head, Main, NextScript } from 'next/document'; 3 | import { generateCssVariables } from '../utils/theme-utils'; 4 | 5 | class MyDocument extends Document { 6 | render() { 7 | const cssVars = generateCssVariables(); 8 | 9 | return ( 10 | 11 | 12 | 13 | 14 | 17 |
18 | 19 | 20 | 21 | ); 22 | } 23 | } 24 | 25 | export default MyDocument; 26 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2019", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "strict": false, 12 | "forceConsistentCasingInFileNames": true, 13 | "noEmit": true, 14 | "esModuleInterop": true, 15 | "module": "esnext", 16 | "moduleResolution": "node", 17 | "resolveJsonModule": true, 18 | "isolatedModules": true, 19 | "jsx": "react-jsx", 20 | "incremental": true, 21 | "paths": { 22 | "@/*": [ 23 | "./src/*" 24 | ] 25 | }, 26 | "baseUrl": "." 27 | }, 28 | "include": [ 29 | "next-env.d.ts", 30 | "**/*.ts", 31 | "**/*.tsx" 32 | ], 33 | "exclude": [ 34 | "node_modules" 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /utils/theme-utils.js: -------------------------------------------------------------------------------- 1 | const { COLOR_THEMES, FONT_THEMES } = require('../themes'); 2 | 3 | const THEME = process.env.BLOG_THEME || 'default'; 4 | const FONT_HEADINGS = process.env.BLOG_FONT_HEADINGS || 'sans-serif'; 5 | const FONT_BODY = process.env.BLOG_FONT_BODY || 'sans-serif'; 6 | 7 | export function generateCssVariables() { 8 | const cssVars = {}; 9 | const themeColors = COLOR_THEMES[THEME]?.colors || {}; 10 | for (const [key, value] of Object.entries(themeColors)) { 11 | cssVars[`--theme-${key}`] = value; 12 | } 13 | cssVars['--theme-headings'] = FONT_THEMES[FONT_HEADINGS] || 'sans-serif'; 14 | cssVars['--theme-body'] = FONT_THEMES[FONT_BODY] || 'sans-serif'; 15 | 16 | const cssVarsString = Object.entries(cssVars) 17 | .map(([key, value]) => `${key}: ${value};`) 18 | .join('\n'); 19 | 20 | return cssVarsString; 21 | } 22 | -------------------------------------------------------------------------------- /posts/example-post-4.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | type: Post 3 | title: Mental models for designers 4 | description: "Consider that for a moment: everything we see around us is assumed to have had a cause and is contingent upon." 5 | date: '2024-08-05' 6 | --- 7 | 8 | Mental models are frameworks people create in their minds to understand how something should work. They’re the expectations users carry with them when they encounter a new app, website, or product. For example, a user expects that clicking on a logo will take them back to the homepage, or that clicking a shopping cart icon will show them a list of items they’ve added. 9 | 10 | Mental models vary from user to user, depending on factors like cultural background, experience level, and personal habits. However, successful designs usually align with widely shared expectations, allowing for intuitive interactions. 11 | 12 | This is an example post. There's another one [here](/posts/example-post). 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 nextjs-blog-theme 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /stackbit.config.ts: -------------------------------------------------------------------------------- 1 | // stackbit.config.ts 2 | import { defineStackbitConfig } from '@stackbit/types'; 3 | import { GitContentSource } from '@stackbit/cms-git'; 4 | 5 | export default defineStackbitConfig({ 6 | stackbitVersion: '~0.6.0', 7 | ssgName: 'nextjs', 8 | nodeVersion: '18', 9 | contentSources: [ 10 | new GitContentSource({ 11 | rootPath: __dirname, 12 | contentDirs: ['posts'], 13 | models: [ 14 | { 15 | name: "Post", 16 | type: "page", 17 | urlPath: "/posts/{slug}", 18 | filePath: "posts/{slug}.mdx", 19 | fields: [ 20 | { name: "title", type: "string", required: true, default: 'Post Title' }, 21 | { name: "description", type: "string", default: 'Post description goes here' }, 22 | { name: "date", type: "date", required: true }, 23 | ] 24 | } 25 | ], 26 | assetsConfig: { 27 | referenceType: 'static', 28 | staticDir: 'public', 29 | uploadDir: 'images', 30 | publicPath: '/' 31 | } 32 | }) 33 | ] 34 | }); -------------------------------------------------------------------------------- /public/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /posts/example-post-2.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | type: Post 3 | title: Next.js and Netlify - The Perfect Duo for Modern Web Development 4 | description: >- 5 | Consider that for a moment: everything we see around us is assumed to have had 6 | a cause and is contingent upon. 7 | date: '2024-10-05' 8 | --- 9 | 10 | **This is an example post. There's another one [here](/posts/example-post-1).** 11 | 12 | Combining **Next.js** and **Netlify** offers a powerful solution for building and deploying modern web applications with ease. Next.js, known for its versatility in handling both static and dynamic content, is a favorite for developers who need a React-based framework that supports server-side rendering, static site generation, and API routes—all essential for dynamic, SEO-friendly sites. 13 | 14 | Pairing this with Netlify’s streamlined deployment workflow, continuous integration, and serverless functions provides a smooth, fast, and secure development-to-deployment process. Netlify’s built-in features for **automatic deployment** from Git, **CDN distribution**, and **edge functions** mean Next.js apps can scale easily while maintaining high performance. 15 | 16 | For UI designers and developers, this combo enables rapid iteration, easier A/B testing, and optimized performance without the DevOps overhead. Plus, the ability to leverage **dynamic routing and API integrations** means more control over design and functionality. 17 | 18 | ## Additional resources 19 | 20 | - [Next.js on Netlify](https://docs.netlify.com/frameworks/next-js/overview/) 21 | - [Use Visual Editor with Next.js](https://docs.netlify.com/visual-editor/frameworks/next/) -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nextjs-blog-theme", 3 | "description": "A customizable Next.js and Tailwind blog starter. Designed by the Bejamas agency.", 4 | "author": "Bejamas and Netlify Inc.", 5 | "contributors": [ 6 | "Charlie Gerard", 7 | "Prince Wilson", 8 | "Tara Manicsic", 9 | "Thom Krupa", 10 | "Tomas Bankauskas" 11 | ], 12 | "homepage": "https://github.com/netlify-templates/nextjs-blog-theme", 13 | "bugs": { 14 | "url": "https://github.com/netlify-templates/nextjs-blog-theme/issues" 15 | }, 16 | "scripts": { 17 | "dev": "next", 18 | "dev:watch": "next-remote-watch ./posts", 19 | "build": "next build", 20 | "start": "next start", 21 | "export": "next build && next export", 22 | "lint": "next lint" 23 | }, 24 | "dependencies": { 25 | "@mapbox/rehype-prism": "^0.9.0", 26 | "@tailwindcss/typography": "^0.5.19", 27 | "classnames": "^2.3.1", 28 | "gray-matter": "^4.0.3", 29 | "next": "^16.0.8", 30 | "next-mdx-remote": "^5.0.0", 31 | "next-remote-watch": "2.0.0", 32 | "prismjs": "^1.29.0", 33 | "react": "^19.2.1", 34 | "react-dom": "^19.2.1", 35 | "rehype-unwrap-images": "^1.0.0", 36 | "remark-gfm": "^4.0.0" 37 | }, 38 | "devDependencies": { 39 | "@stackbit/cms-git": "^1.0.32", 40 | "@stackbit/types": "^2.0.1", 41 | "@tailwindcss/postcss": "^4.0.16", 42 | "eslint": "^9.0.0", 43 | "eslint-config-next": "^16.0.8", 44 | "eslint-config-prettier": "^9.1.0", 45 | "postcss": "^8.4.38", 46 | "tailwindcss": "^4.0.16", 47 | "typescript": "^5.1.3" 48 | }, 49 | "license": "MIT" 50 | } 51 | -------------------------------------------------------------------------------- /themes.js: -------------------------------------------------------------------------------- 1 | exports.COLOR_THEMES = { 2 | default: { 3 | colors: { 4 | primary: '#7D7AFF', 5 | 'gradient-1': '#7d7aff', 6 | 'gradient-2': '#2121e2', 7 | 'gradient-3': '#00fff0', 8 | 'gradient-4': '#8785FF', 9 | }, 10 | }, 11 | bejamas: { 12 | colors: { 13 | primary: '#FF8585', 14 | 'gradient-1': '#7d7aff', 15 | 'gradient-2': '#2121E2', 16 | 'gradient-3': '#FF76B8', 17 | 'gradient-4': '#001AFF', 18 | }, 19 | }, 20 | netlify: { 21 | colors: { 22 | primary: '#00A354', 23 | 'gradient-1': '#00F0FF', 24 | 'gradient-2': '#00F0FF', 25 | 'gradient-3': '#FAFF00', 26 | 'gradient-4': '#00F0FF', 27 | }, 28 | }, 29 | reddie: { 30 | colors: { 31 | primary: '#FF4D4D', 32 | 'gradient-1': '#FFC700', 33 | 'gradient-2': '#FF85DD', 34 | 'gradient-3': '#FF85DD', 35 | 'gradient-4': '#FF8585', 36 | }, 37 | }, 38 | greenie: { 39 | colors: { 40 | primary: '#C78500', 41 | 'gradient-1': '#FFCC81', 42 | 'gradient-2': '#00F37F', 43 | 'gradient-3': '#00F37F', 44 | 'gradient-4': '#FFCC81', 45 | }, 46 | }, 47 | }; 48 | 49 | exports.FONT_THEMES = { 50 | 'sans-serif': `ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"`, 51 | serif: `ui-serif, Georgia, Cambria, "Times New Roman", Times, serif`, 52 | monospace: `ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace`, 53 | }; 54 | -------------------------------------------------------------------------------- /components/Layout.js: -------------------------------------------------------------------------------- 1 | import classNames from 'classnames'; 2 | import { useEffect } from 'react'; 3 | import styles from './Layout.module.css'; 4 | 5 | export function GradientBackground({ variant, className }) { 6 | const classes = classNames( 7 | { 8 | [styles.colorBackground]: variant === 'large', 9 | [styles.colorBackgroundBottom]: variant === 'small', 10 | }, 11 | className 12 | ); 13 | 14 | return
; 15 | } 16 | 17 | export default function Layout({ children }) { 18 | const setAppTheme = () => { 19 | const darkMode = localStorage.getItem('theme') === 'dark'; 20 | const lightMode = localStorage.getItem('theme') === 'light'; 21 | 22 | if (darkMode) { 23 | document.documentElement.classList.add('dark'); 24 | } else if (lightMode) { 25 | document.documentElement.classList.remove('dark'); 26 | } 27 | return; 28 | }; 29 | 30 | const handleSystemThemeChange = () => { 31 | var darkQuery = window.matchMedia('(prefers-color-scheme: dark)'); 32 | 33 | darkQuery.onchange = (e) => { 34 | if (e.matches) { 35 | document.documentElement.classList.add('dark'); 36 | localStorage.setItem('theme', 'dark'); 37 | } else { 38 | document.documentElement.classList.remove('dark'); 39 | localStorage.setItem('theme', 'light'); 40 | } 41 | }; 42 | }; 43 | 44 | useEffect(() => { 45 | setAppTheme(); 46 | }, []); 47 | 48 | useEffect(() => { 49 | handleSystemThemeChange(); 50 | }, []); 51 | 52 | return ( 53 |
54 |
55 | {children} 56 |
57 |
58 | ); 59 | } 60 | -------------------------------------------------------------------------------- /components/Layout.module.css: -------------------------------------------------------------------------------- 1 | .colorBackground { 2 | left: 50%; 3 | transform: translateX(-50%); 4 | background: radial-gradient( 5 | at 71% 77%, 6 | var(--color-gradient-1) 0, 7 | transparent 21% 8 | ), 9 | radial-gradient(at 36% 47%, var(--color-gradient-3) 0, transparent 50%), 10 | radial-gradient(at 54% 29%, var(--color-gradient-3) 0, transparent 28%), 11 | radial-gradient(at 45% 51%, var(--color-gradient-1) 0, transparent 53%), 12 | radial-gradient(at 73% 44%, var(--color-gradient-2) 0, transparent 54%), 13 | radial-gradient(at 24% 7%, var(--color-gradient-2) 0, transparent 40%), 14 | radial-gradient(at 76% 46%, var(--color-gradient-1) 0, transparent 50%); 15 | /* mix-blend-mode: normal; */ 16 | max-height: 800px; 17 | height: 80vh; 18 | max-width: 1400px; 19 | width: 70vw; 20 | width: 100%; 21 | filter: blur(44px); 22 | z-index: -1; 23 | } 24 | 25 | .colorBackgroundBottom { 26 | left: 50%; 27 | transform: translateX(-50%) rotate(190deg); 28 | background: radial-gradient( 29 | at 83% 25%, 30 | var(--color-gradient-1) 0, 31 | transparent 21% 32 | ), 33 | radial-gradient(at 36% 47%, var(--color-gradient-3) 0, transparent 50%), 34 | radial-gradient(at 79% 45%, var(--color-gradient-3) 0, transparent 28%), 35 | radial-gradient(at 66% 38%, var(--color-gradient-1) 0, transparent 53%), 36 | radial-gradient(at 89% 13%, var(--color-gradient-2) 0, transparent 54%), 37 | radial-gradient(at 24% 7%, var(--color-gradient-2) 0, transparent 40%), 38 | radial-gradient(at 76% 46%, var(--color-gradient-1) 0, transparent 50%); 39 | /* mix-blend-mode: normal; */ 40 | height: 600px; 41 | max-width: 900px; 42 | width: 55vw; 43 | width: 100%; 44 | filter: blur(44px); 45 | z-index: -1; 46 | } 47 | -------------------------------------------------------------------------------- /posts/example-post-3.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | type: Post 3 | title: Why Next.js Works Great with Netlify 4 | description: "If you're using Next.js and Netlify, you’re tapping into a powerhouse combination for fast, reliable, and scalable web applications." 5 | date: '2024-09-05' 6 | --- 7 | 8 | If you're using **Next.js** and **Netlify**, you’re tapping into a powerhouse combination for fast, reliable, and scalable web applications. 9 | 10 | **This is an example post. There's another one [here](/posts/example-post-1).** 11 | 12 | Here’s why they work so well together: 13 | 14 | 1. **Seamless Deployment**: With Netlify’s Git-based deployment, pushing updates to a Next.js site is automatic and effortless. Every change pushed to GitHub or GitLab triggers a fresh build and deploys your site without any manual effort, perfect for fast-paced projects. 15 | 16 | 2. **Static and Dynamic Content Flexibility**: Next.js shines by allowing both static generation (SSG) and server-side rendering (SSR), and Netlify makes deploying either easy. Static content is globally cached on Netlify’s CDN, making your site lightning-fast, while Netlify Functions handle any server-side needs with zero configuration. 17 | 18 | 3. **Serverless Functions**: Netlify’s serverless functions work beautifully with Next.js API routes, enabling dynamic features (like forms, authentication, and data fetching) without additional servers. This lets you build interactive, backend-supported sites with just front-end code. 19 | 20 | 4. **Edge Caching and CDNs**: Next.js’s pages are distributed across Netlify’s global CDN, ensuring that your users get a fast experience no matter where they are. This setup optimizes load times and enhances site performance. 21 | 22 | 5. **Developer-Friendly Workflow**: Both Next.js and Netlify focus on a developer-friendly experience. Local development, previews, and rollbacks are simple, so you can focus on building a better user experience without worrying about the infrastructure. 23 | 24 | For anyone building with Next.js, deploying on Netlify means you get the best of **speed, reliability, and ease**—all the ingredients needed to create high-performing, user-friendly web apps! 25 | 26 | ## Additional resources 27 | 28 | - [Next.js on Netlify](https://docs.netlify.com/frameworks/next-js/overview/) 29 | - [Use Visual Editor with Next.js](https://docs.netlify.com/visual-editor/frameworks/next/) -------------------------------------------------------------------------------- /components/Footer.js: -------------------------------------------------------------------------------- 1 | const sunIcon = ( 2 | 10 | 17 | 18 | 19 | 20 | 21 | 26 | 27 | 28 | 29 | ); 30 | 31 | const moonIcon = ( 32 | 39 | 47 | 48 | ); 49 | 50 | const ThemeSwitcher = () => { 51 | return ( 52 |
53 | 64 | 65 | 76 |
77 | ); 78 | }; 79 | 80 | export default function Footer({ copyrightText }) { 81 | return ( 82 |
83 |

84 | {copyrightText} 85 |

86 | 87 |
88 | ); 89 | } 90 | -------------------------------------------------------------------------------- /pages/index.js: -------------------------------------------------------------------------------- 1 | import Link from 'next/link'; 2 | import { getPosts } from '../utils/mdx-utils'; 3 | 4 | import Footer from '../components/Footer'; 5 | import Header from '../components/Header'; 6 | import Layout, { GradientBackground } from '../components/Layout'; 7 | import ArrowIcon from '../components/ArrowIcon'; 8 | import { getGlobalData } from '../utils/global-data'; 9 | import SEO from '../components/SEO'; 10 | 11 | export default function Index({ posts, globalData }) { 12 | return ( 13 | 14 | 15 |
16 |
17 |

18 | {globalData.blogTitle} 19 |

20 |
    21 | {posts.map((post) => ( 22 |
  • 27 | 32 | {post.data.date && ( 33 |

    37 | {post.data.date} 38 |

    39 | )} 40 |

    41 | {post.data.title} 42 |

    43 | {post.data.description && ( 44 |

    48 | {post.data.description} 49 |

    50 | )} 51 | 52 | 53 |
  • 54 | ))} 55 |
56 |
57 |