├── public ├── .nojekyll ├── vercel.svg ├── window.svg ├── file.svg ├── globe.svg └── next.svg ├── .github ├── FUNDING.yml ├── dependabot.yml ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── deploy.yml ├── app ├── favicon.ico ├── globals.css ├── layout.tsx └── page.tsx ├── postcss.config.mjs ├── eslint.config.mjs ├── .gitignore ├── package.json ├── tsconfig.json ├── next.config.ts ├── LICENSE └── README.md /public/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: gregrickaby 4 | -------------------------------------------------------------------------------- /app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gregrickaby/nextjs-github-pages/HEAD/app/favicon.ico -------------------------------------------------------------------------------- /postcss.config.mjs: -------------------------------------------------------------------------------- 1 | const config = { 2 | plugins: { 3 | "@tailwindcss/postcss": {}, 4 | }, 5 | }; 6 | 7 | export default config; 8 | -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | 2 | version: 2 3 | updates: 4 | - package-ecosystem: 'npm' 5 | directory: '/' 6 | schedule: 7 | interval: 'weekly' 8 | day: 'monday' 9 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # PR Title 2 | 3 | Closes 4 | 5 | ## Description 6 | 7 | Please describe what did you built or fixed? 8 | 9 | ## Screenshot 10 | 11 | Could you please drop a screenshot of your feature or fix? 12 | 13 | ## Steps to Verify 14 | 15 | Please walk through how to verify and test this feature or fix. 16 | -------------------------------------------------------------------------------- /public/window.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/file.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig, globalIgnores } from "eslint/config"; 2 | import nextVitals from "eslint-config-next/core-web-vitals"; 3 | import nextTs from "eslint-config-next/typescript"; 4 | 5 | const eslintConfig = defineConfig([ 6 | ...nextVitals, 7 | ...nextTs, 8 | // Override default ignores of eslint-config-next. 9 | globalIgnores([ 10 | // Default ignores of eslint-config-next: 11 | ".next/**", 12 | "out/**", 13 | "build/**", 14 | "next-env.d.ts", 15 | ]), 16 | ]); 17 | 18 | export default eslintConfig; 19 | -------------------------------------------------------------------------------- /app/globals.css: -------------------------------------------------------------------------------- 1 | @import "tailwindcss"; 2 | 3 | :root { 4 | --background: #ffffff; 5 | --foreground: #171717; 6 | } 7 | 8 | @theme inline { 9 | --color-background: var(--background); 10 | --color-foreground: var(--foreground); 11 | --font-sans: var(--font-geist-sans); 12 | --font-mono: var(--font-geist-mono); 13 | } 14 | 15 | @media (prefers-color-scheme: dark) { 16 | :root { 17 | --background: #0a0a0a; 18 | --foreground: #ededed; 19 | } 20 | } 21 | 22 | body { 23 | background: var(--background); 24 | color: var(--foreground); 25 | font-family: Arial, Helvetica, sans-serif; 26 | } 27 | -------------------------------------------------------------------------------- /.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.* 7 | .yarn/* 8 | !.yarn/patches 9 | !.yarn/plugins 10 | !.yarn/releases 11 | !.yarn/versions 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 | .pnpm-debug.log* 32 | 33 | # env files (can opt-in for committing if needed) 34 | .env* 35 | 36 | # vercel 37 | .vercel 38 | 39 | # typescript 40 | *.tsbuildinfo 41 | next-env.d.ts 42 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nextjs-github-pages", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "eslint" 10 | }, 11 | "dependencies": { 12 | "next": "^16.1.0", 13 | "react": "^19.2.3", 14 | "react-dom": "^19.2.3" 15 | }, 16 | "devDependencies": { 17 | "@tailwindcss/postcss": "^4.1.18", 18 | "@types/node": "^25.0.3", 19 | "@types/react": "^19.2.7", 20 | "@types/react-dom": "^19.2.3", 21 | "eslint": "^9.39.2", 22 | "eslint-config-next": "^16.1.0", 23 | "tailwindcss": "^4.1.18", 24 | "typescript": "^5.9.3" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2017", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noEmit": true, 9 | "esModuleInterop": true, 10 | "module": "esnext", 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "jsx": "react-jsx", 15 | "incremental": true, 16 | "plugins": [ 17 | { 18 | "name": "next" 19 | } 20 | ], 21 | "paths": { 22 | "@/*": ["./*"] 23 | } 24 | }, 25 | "include": [ 26 | "next-env.d.ts", 27 | "**/*.ts", 28 | "**/*.tsx", 29 | ".next/types/**/*.ts", 30 | ".next/dev/types/**/*.ts", 31 | "**/*.mts" 32 | ], 33 | "exclude": ["node_modules"] 34 | } 35 | -------------------------------------------------------------------------------- /next.config.ts: -------------------------------------------------------------------------------- 1 | import type { NextConfig } from "next"; 2 | 3 | const nextConfig: NextConfig = { 4 | /** 5 | * Enable static exports. 6 | * 7 | * @see https://nextjs.org/docs/app/building-your-application/deploying/static-exports 8 | */ 9 | output: "export", 10 | 11 | /** 12 | * Set base path. This is usually the slug of your repository. 13 | * 14 | * @see https://nextjs.org/docs/app/api-reference/next-config-js/basePath 15 | */ 16 | basePath: "/nextjs-github-pages", 17 | 18 | /** 19 | * Disable server-based image optimization. Next.js does not support 20 | * dynamic features with static exports. 21 | * 22 | * @see https://nextjs.org/docs/pages/api-reference/components/image#unoptimized 23 | */ 24 | images: { 25 | unoptimized: true, 26 | }, 27 | }; 28 | 29 | export default nextConfig; 30 | -------------------------------------------------------------------------------- /app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Geist, Geist_Mono } from "next/font/google"; 3 | import "./globals.css"; 4 | 5 | const geistSans = Geist({ 6 | variable: "--font-geist-sans", 7 | subsets: ["latin"], 8 | }); 9 | 10 | const geistMono = Geist_Mono({ 11 | variable: "--font-geist-mono", 12 | subsets: ["latin"], 13 | }); 14 | 15 | export const metadata: Metadata = { 16 | title: "Create Next App", 17 | description: "Generated by create next app", 18 | }; 19 | 20 | export default function RootLayout({ 21 | children, 22 | }: Readonly<{ 23 | children: React.ReactNode; 24 | }>) { 25 | return ( 26 | 27 | 30 | {children} 31 | 32 | 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /public/globe.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/page.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | 3 | export default function Home() { 4 | return ( 5 |
6 |
7 | Next.js logo 15 |
16 |

17 | To get started, edit the page.tsx file. 18 |

19 |

20 | Looking for a starting point or more instructions? Head over to{" "} 21 | 25 | Templates 26 | {" "} 27 | or the{" "} 28 | 32 | Learning 33 | {" "} 34 | center. 35 |

36 |
37 |
38 | 44 | Vercel logomark 51 | Deploy Now 52 | 53 | 59 | Documentation 60 | 61 |
62 |
63 |
64 | ); 65 | } 66 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | # Sample workflow for building and deploying a Next.js site to GitHub Pages 2 | # 3 | # To get started with Next.js see: https://nextjs.org/docs/getting-started 4 | # 5 | name: Deploy Next.js site to Pages 6 | 7 | on: 8 | # Runs on pushes targeting the default branch 9 | push: 10 | branches: ["main"] 11 | 12 | # Allows you to run this workflow manually from the Actions tab 13 | workflow_dispatch: 14 | 15 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 16 | permissions: 17 | contents: read 18 | pages: write 19 | id-token: write 20 | 21 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 22 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 23 | concurrency: 24 | group: "pages" 25 | cancel-in-progress: false 26 | 27 | jobs: 28 | # Build job 29 | build: 30 | runs-on: ubuntu-latest 31 | steps: 32 | - name: Checkout 33 | uses: actions/checkout@v4 34 | 35 | - name: Detect package manager 36 | id: detect-package-manager 37 | run: | 38 | if [ -f "${{ github.workspace }}/yarn.lock" ]; then 39 | echo "manager=yarn" >> $GITHUB_OUTPUT 40 | echo "command=install" >> $GITHUB_OUTPUT 41 | echo "runner=yarn" >> $GITHUB_OUTPUT 42 | exit 0 43 | elif [ -f "${{ github.workspace }}/pnpm-lock.yaml" ]; then 44 | echo "manager=pnpm" >> $GITHUB_OUTPUT 45 | echo "command=install" >> $GITHUB_OUTPUT 46 | echo "runner=pnpm" >> $GITHUB_OUTPUT 47 | npm install -g pnpm 48 | exit 0 49 | elif [ -f "${{ github.workspace }}/package.json" ]; then 50 | echo "manager=npm" >> $GITHUB_OUTPUT 51 | echo "command=ci" >> $GITHUB_OUTPUT 52 | echo "runner=npx --no-install" >> $GITHUB_OUTPUT 53 | exit 0 54 | else 55 | echo "Unable to determine package manager" 56 | exit 1 57 | fi 58 | 59 | - name: Setup Node 60 | uses: actions/setup-node@v4 61 | with: 62 | node-version: "lts/*" 63 | cache: ${{ steps.detect-package-manager.outputs.manager }} 64 | 65 | - name: Setup Pages 66 | uses: actions/configure-pages@v4 67 | 68 | - name: Restore cache 69 | uses: actions/cache@v4 70 | with: 71 | path: | 72 | .next/cache 73 | # Generate a new cache whenever packages or source files change. 74 | key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }} 75 | # If source files changed but packages didn't, rebuild from a prior cache. 76 | restore-keys: | 77 | ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}- 78 | 79 | - name: Install dependencies 80 | run: ${{ steps.detect-package-manager.outputs.manager }} ${{ steps.detect-package-manager.outputs.command }} 81 | 82 | - name: Build with Next.js 83 | run: ${{ steps.detect-package-manager.outputs.runner }} next build 84 | 85 | - name: Upload artifact 86 | uses: actions/upload-pages-artifact@v3 87 | with: 88 | path: ./out 89 | 90 | # Deployment job 91 | deploy: 92 | environment: 93 | name: github-pages 94 | url: ${{ steps.deployment.outputs.page_url }} 95 | runs-on: ubuntu-latest 96 | needs: build 97 | steps: 98 | - name: Deploy to GitHub Pages 99 | id: deployment 100 | uses: actions/deploy-pages@v4 101 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Next.js GitHub Pages 2 | 3 | Deploy Next.js to GitHub Pages with GitHub Actions. [View the deployed app](https://gregrickaby.github.io/nextjs-github-pages/) 🚀 4 | 5 | > ⚠️ Heads up! GitHub Pages _is not_ a Node.js server. So dynamic logic that cannot be computed during the build process, are not supported! See all the [unsupported features](https://nextjs.org/docs/app/building-your-application/deploying/static-exports#unsupported-features). 6 | 7 | --- 8 | 9 | ## Configure Next.js 10 | 11 | ### Next.js Config 12 | 13 | First, you need to configure Next.js to [deploy static exports](https://nextjs.org/docs/app/building-your-application/deploying/static-exports). This is required for GitHub Pages to work. 14 | 15 | 1. Open the `next.config.ts` file 16 | 2. Add the following: 17 | 18 | ```typescript 19 | import type { NextConfig } from "next"; 20 | 21 | const nextConfig: NextConfig = { 22 | /** 23 | * Enable static exports. 24 | * 25 | * @see https://nextjs.org/docs/app/building-your-application/deploying/static-exports 26 | */ 27 | output: "export", 28 | 29 | /** 30 | * Set base path. This is the slug of your GitHub repository. 31 | * 32 | * @see https://nextjs.org/docs/app/api-reference/next-config-js/basePath 33 | */ 34 | basePath: "/nextjs-github-pages", 35 | 36 | /** 37 | * Disable server-based image optimization. Next.js does not support 38 | * dynamic features with static exports. 39 | * 40 | * @see https://nextjs.org/docs/app/api-reference/components/image#unoptimized 41 | */ 42 | images: { 43 | unoptimized: true, 44 | }, 45 | }; 46 | 47 | export default nextConfig; 48 | ``` 49 | 50 | 3. Save the `next.config.ts` 51 | 52 | 4. Finally, place a `.nojekyll` file in the `/public` directory to disable GitHub Pages from trying to create a [Jekyll](https://github.blog/2009-12-29-bypassing-jekyll-on-github-pages/) website. 53 | 54 | ```treeview 55 | . 56 | ├── app/ 57 | ├── public/ 58 | │ └── .nojekyll 59 | ├── next.config.ts 60 | ``` 61 | 62 | Perfect! This is all you need to configure Next.js to deploy on GitHub Pages. 63 | 64 | ### Add base path to `page.tsx` 65 | 66 | Next, you will need to add the base path to images in `page.tsx` file. This is required for the images to appear on GitHub Pages. 67 | 68 | 1. Open `app/page.tsx` 69 | 2. Find the `Image` components 70 | 3. Add `/nextjs-github-pages/` (or the slug of your GitHub repository) to the `src` prop: 71 | 72 | ```tsx 73 | Vercel Logo 81 | ``` 82 | 83 | 4. Save the `page.tsx` file 84 | 85 | Learn more by reading the official documentation [for basePath and images](https://nextjs.org/docs/app/api-reference/config/next-config-js/basePath#images). 86 | 87 | --- 88 | 89 | ## Configure GitHub Repository 90 | 91 | Now that Next.js is configured, you need to set up your GitHub repository to deploy to GitHub Pages. 92 | 93 | ### Setup GitHub Action 94 | 95 | This is where the magic happens! This [workflow file](https://github.com/gregrickaby/nextjs-github-pages/blob/main/.github/workflows/deploy.yml) will automatically build and deploy the app when you push to the `main` branch. 96 | 97 | 1. Create `.github/workflows/deploy.yml` file 98 | 2. Paste the contents of 99 | 3. Save the `deploy.yml` file 100 | 101 | ### Enable GitHub Pages 102 | 103 | 1. Go to your repository's **Settings** tab 104 | 2. Click "Pages" in the sidebar 105 | 3. Under "Build and Deployment", select "GitHub Actions" as the source: 106 | 107 | ![screenshot of github pages settings](https://dl.dropboxusercontent.com/s/vf74zv2wcepnt9w/Screenshot%202025-02-03%20at%2021.10.06.png?dl=0) 108 | 109 | ### Push to GitHub 110 | 111 | Now that everything is configured, you can commit your code and push to GitHub. This will trigger the GitHub Action workflow and deploy your app to GitHub Pages. 112 | 113 | ```bash 114 | git add . && git commit -m "initial commit" && git push 115 | ``` 116 | 117 | You should see your site deployed to GitHub Pages in a few minutes. 🚀 118 | 119 | --- 120 | 121 | ## Wrap up 122 | 123 | Thanks for reading and I hope this helps. If you noticed something wrong, please [open an issue](https://github.com/gregrickaby/nextjs-github-pages/issues). Cheers! 🍻 124 | 125 | --- 126 | --------------------------------------------------------------------------------