Other deployment options

Updated on

At the moment, the Shipixen app only supports 1-click deployments to Vercel.

However, since the result is a static Next.js app, it can potentially be deployed to any static hosting service.

It is possible to download the generated boilerplate as a zip file and deploy it to other hosting services.

Here are some examples of how to deploy the app to other hosting services:

Netlify

Netlify’s Next.js runtime configures enables key Next.js functionality on your website without the need for additional configurations. Netlify generates serverless functions that will handle Next.js functionalities such as server-side rendered (SSR) pages, incremental static regeneration (ISR), next/images, etc.

See Next.js on Netlify for suggested configuration values and more details.

Static hosting services / GitHub Pages / S3 / Firebase etc.

  1. Add output: 'export' and in { images: { unoptimized: true } } next.config.js. See static exports documentation for more information.
next.config.js
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;
    },
  });
};

  1. Comment out headers() from next.config.js.

  2. Change components/shared/Image.tsx to use a standard <img> tag instead of next/image:

components/shared/Image.tsx
- 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.

  1. 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)

  2. Run npm run build. The generated static content is in the out folder.

  3. Deploy the out folder to your hosting service of choice or run npx 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:

data/sample.mdx
---
title: Sample
tags:
 - sample
date: 2024-01-01
summary: A sample post
---

Deploy to Github Pages

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