next.jscorsstyled-componentsstatic-site-generation

Global CSS is not properly loading with SSG in Next JS - CORS error?


I'm using React, Next JS, and styled-components. Right now I've followed the documentation and have the following file structure. I want to simply get a global CSS working on a STATIC HTML page. I expected Next JS's SSG to work properly. Please DO NOT tell me to use a server. I know that's an easy fix. I want to get this working on a local static page I open from my file explorer.

pages/
    _app.tsx
    _document.tsx
    index.tsx
styles/core.css

I am very simply importing a core.css in my _app.tsx

import { AppProps } from 'next/app';
import '../styles/core.css';

function MyApp({ Component, pageProps }: AppProps) {
    return <Component {...pageProps} />
}

export default MyApp;

Now when I go and generate a page via SSG (I do not want to use a server), the generated HTML file has these 2 lines in it

<link rel="preload" href="/_next/static/css/4da684a704d068a3.css" as="style" crossorigin=""/>
<link rel="stylesheet" href="/_next/static/css/4da684a704d068a3.css" crossorigin="" data-n-g=""/>

When I go to open the index.html the console shows this error

Access to CSS stylesheet at 'file:///C:/_next/static/css/4da684a704d068a3.css' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, isolated-app, chrome-extension, chrome-untrusted, https, edge.

How do I solve this issue? Changing the HTML line to

<link rel="stylesheet" href="_next/static/css/4da684a704d068a3.css">

makes it work. Please help, I cannot find anything online.

Here's my _document.tsx if it helps

import Document, { Head, Html, Main, NextScript, DocumentContext, DocumentInitialProps } from 'next/document'
import { ServerStyleSheet } from 'styled-components'

export default class MyDocument extends Document {
    render() {
        return (
            <Html lang="en">
                <Head />
                <body>
                    <Main />
                    <NextScript />
                </body>
            </Html>
        )
    }

    static async getInitialProps(ctx: DocumentContext): Promise<DocumentInitialProps> {
        const sheet = new ServerStyleSheet()
        const originalRenderPage = ctx.renderPage

        try {
            ctx.renderPage = () => originalRenderPage({
                enhanceApp: (App) => (props) => sheet.collectStyles(<App {...props} />),
            })

            const initialProps = await Document.getInitialProps(ctx)
            return {
                ...initialProps,
                styles: (
                    <>
                        {initialProps.styles}
                        {sheet.getStyleElement()}
                    </>
                ),
            }
        } finally {
            sheet.seal()
        }
    }
}

Here's my config

/** @type {import('next').NextConfig} */
const nextConfig = {
    reactStrictMode: true,
    output: 'export',
    compiler: {
        styledComponents: true,
    },
    
}
  
module.exports = nextConfig

Solution

  • I wish there were a better solution. Here's what I ended up doing if anyone else ever faces this same niche issue.

    const fs = require('fs');
    const cheerio = require('cheerio');
    
    // Read the generated HTML file
    const html = fs.readFileSync('./out/target.html', 'utf8');
    
    // Load the HTML into cheerio
    const $ = cheerio.load(html);
    
    // Find all <link> tags with crossorigin attribute and remove it
    $('link[crossorigin]').removeAttr('crossorigin');
    $('script[crossorigin]').removeAttr('crossorigin');
    
    // Write the modified HTML back to the file
    fs.writeFileSync('./out/target.html', $.html(), 'utf8');

    I run this JS file whenever I build.

    next build && node ./scripts/modify_html.js