Deploying to Static hosting services / Edge / GitHub Pages / S3 / Firebase etc.
- Updated on
It is possible to deploy the app to any static hosting service. However, that may require additional configuration to support Next.js features like server-side rendering (SSR), incremental static regeneration (ISR), next/image
, etc.
Of course, you might be able to host this at a much lower cost than a server-based hosting service. This could be a good choice for a blog or a small landing page.
Changes required for static hosting services
When deploying to static hosting services, you need to make some changes to your Shipixen/Next.js project to ensure you're not using server-side functions or features that require a server.
- Add
output: 'export'
and in{ images: { unoptimized: true } }
next.config.js
. See static exports documentation for more information.
onst { withContentlayer } = require('@shipixen/next-contentlayer-module');
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true',
});
-// You might need to insert additional domains in script-src if you are using external services
-const ContentSecurityPolicy = `...`
-const securityHeaders = [...]
/**
* @type {import('next/dist/next-server/server/config').NextConfig}
**/
module.exports = () => {
const plugins = [withContentlayer, withBundleAnalyzer];
return plugins.reduce((acc, next) => next(acc), {
+ output: 'export',
reactStrictMode: true,
pageExtensions: ['ts', 'tsx', 'js', 'jsx', 'md', 'mdx'],
eslint: {
dirs: ['app', 'components', 'layouts', 'scripts'],
},
images: {
+ unoptimized: true,
remotePatterns: [
{
protocol: 'https',
hostname: 'picsum.photos',
port: '',
pathname: '**/*',
},
{
protocol: 'https',
hostname: 'shipixen.com',
port: '',
pathname: '**/*',
},
],
},
- async headers() {
- return [
- {
- source: '/(.*)',
- headers: securityHeaders,
- },
- ];
- },
webpack: (config, options) => {
config.module.rules.push({
test: /\.svg$/,
use: ['@svgr/webpack'],
});
return config;
},
});
};
Comment out
headers()
fromnext.config.js
.Change
components/shared/Image.tsx
to use a standard<img>
tag instead ofnext/image
:
- import NextImage, { ImageProps } from 'next/image'
+ import { ImageProps } from 'next/image';
- const Image = ({ ...rest }: ImageProps) => <NextImage {...rest} />;
+ // @ts-ignore
+ const Image = ({ ...rest }: ImageProps) => <img {...rest} />
export default Image
Alternatively, to continue using next/image
, you can use an alternative image optimization provider such as Imgix, Cloudinary or Akamai. See image optimization documentation for more details.
Remove
api
folder and components which call the server-side function such as the Newsletter component. Not technically required and the site will build successfully, but the APIs cannot be used as they are server-side functions. If you are using a dynamic Open Graph image, you will also need to change it to a static one. In metadata.js, replace 'socialBanner' with a static image URL (see metadata docs)Run
npm run build
. The generated static content is in theout
folder.Deploy the
out
folder to your hosting service of choice or runnpx serve out
to view the website locally.
👉 Important Static generation needs at least one blog post with a tag to successfully satisfy generateStaticParams()
. This might change in the future, but for now if you see the error Error: Page "<path>" is missing "generateStaticParams()" so it cannot be used with "output: export" config.
, you need to add a blog post to the data
folder. For example:
---
title: Sample
tags:
- sample
date: 2024-01-01
summary: A sample post
---
Deploy to Github Pages via Github Actions
Complete the steps 1 to 6 from Static hosting services above and then:
Specify the base path in next.config.js to match your repository name e.g. basePath: "/shipixen-example" (for github.com/shipixen/shipixen-example)
To automate deployment, you can use the following Github Actions workflow:
# Sample workflow for building and deploying a Next.js site to GitHub Pages
#
# To get started with Next.js see: https://nextjs.org/docs/getting-started
#
name: Deploy Next.js site to Pages
on:
# Runs on pushes targeting the default branch
push:
branches: ['test-gh-pages-static']
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: 'pages'
cancel-in-progress: false
jobs:
# Build job
build:
name: Build Shipixen GH pages
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Detect package manager
id: detect-package-manager
run: |
if [ -f "${{ github.workspace }}/yarn.lock" ]; then
echo "manager=yarn" >> $GITHUB_OUTPUT
echo "command=install" >> $GITHUB_OUTPUT
echo "runner=yarn" >> $GITHUB_OUTPUT
exit 0
elif [ -f "${{ github.workspace }}/package.json" ]; then
echo "manager=npm" >> $GITHUB_OUTPUT
echo "command=ci" >> $GITHUB_OUTPUT
echo "runner=npx --no-install" >> $GITHUB_OUTPUT
exit 0
else
echo "Unable to determine package manager"
exit 1
fi
- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: '18'
cache: ${{ steps.detect-package-manager.outputs.manager }}
- name: Restore cache
uses: actions/cache@v3
with:
path: |
.next/cache
# Generate a new cache whenever packages or source files change.
key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }}
# If source files changed but packages didn't, rebuild from a prior cache.
restore-keys: |
${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}-
- name: Install dependencies
run: ${{ steps.detect-package-manager.outputs.manager }} ${{ steps.detect-package-manager.outputs.command }}
- name: Build project assets
run: ${{ steps.detect-package-manager.outputs.manager }} build
- name: Upload artifact
uses: actions/upload-pages-artifact@v2
with:
path: ./out
# Deployment job
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v2