My Nextjs site builds without error on netlify. But the site looks like this and does not pick up the css. The console shows a syntax error Uncaught SyntaxError: Unexpected token '<'. The routing also does not work.
This site works fine when deployed to vercel. When I run npm run build and npm run start locally it works fine too. My scripts in package.json are as follows
"scripts": {
"dev": "next dev",
"start": "next start",
"build": "next build"
},
Previously I was using next build && next export to build my site but now I need Incremental Static Regeneration which is not supported by next export.
My netlify deploy settings are as
follows
When I use next export a link to any dynamic page that is not in getStaticPaths takes me to the home page. If I go to the url from inside the application, it works fine but a refresh takes me back to the home page. When I remove next export I am faced with the above issue in netlify.
my dynamic page code stripped down is as follows
[tag].js
import Head from "next/head";
import SearchPage from "../../components/app/SearchPage/SearchPage";
import {
fetchAllCuratedTopicsToRender,
fetchTitleAndDescription,
} from "../../services/SearchService";
const Search = ({ title, meta_description }) => {
const defaultTitle = "default title";
const defaultMetaDescription = "default meta description";
return (
<>
<Head>
<title key="title">{title || defaultTitle}</title>
<meta
name="description"
content={meta_description || defaultMetaDescription}
key="description"
/>
</Head>
<SearchPage />
</>
);
};
export default Search;
// Generates all the paths for the curated topics stored in the database
export async function getStaticPaths() {
const APIresponse = await fetchAllCuratedTopicsToRender();
const allPaths = APIresponse.map((el) => {
return { params: { tag: el.keyword } };
});
return {
paths: [...allPaths],
fallback: "blocking",
};
}
export async function getStaticProps(context) {
let titleAndDescription = {
title: "",
meta_description: "",
};
// only get title and description if there is a tag in the url
if (context.params?.tag) {
const tag = context.params.tag.toString();
titleAndDescription = await fetchTitleAndDescription(tag);
}
return {
props: {
title: titleAndDescription.title || "",
meta_description: titleAndDescription.meta_description || "",
},
revalidate: 31536000,
};
}
This would be a case of you having a redirect rule like:
/* /index.html 200
in your public folder. Statically generated sites sometimes needed that for client-side only navigation, but it won't be needed if you're using server-side stuff (including ISR). The Netlify Next.js Runtime also warns about this in your build logs. Simply removing the rule should fix it.
Related
I have a project in Next.js where I have 2 different users: admin and employee.
I have set up 2 folders inside pages; /admin and /users. Each folder/users has its own subpages. for example /admin/dashboard and /employee/dashboard etc
I am trying to obfuscate the role, so that instead of displaying the url: "/admin/dashboard", if the user is an admin, I would like the url to display "/dashboard" which actually gets the "admin/dashboard" page.
I have tried next.js.config and that works perfectly when I do a full page refresh on /dashboard
module.exports = {
async rewrites() {
return [
{
source: '/:path*',
destination: '/admin/:path*',
},
]
},
}
However, if I set my link to "/dashboard", it does not work. I have tried middleware, and I can get "/" to redirect to "/admin", but I want the opposite. ie if I go to "/" it will use the "/admin/" page, but cannot work out how to achieve my goal (if it is possible !)
if (request.nextUrl.pathname.startsWith('/dashboard')) {
url.pathname = "/admin/dashboard";
return NextResponse.redirect(url);
}
Any help greatly appreciated
You can use as from router.push and the Link component.
For example:
import Link from 'next/link';
import { useRouter } from 'next/router';
export default function Example() {
const router = useRouter();
const handleEmployeeClick = () => {
// will show page from /employee/dashboard but show as /dashboard
router.push('/employee/dashboard', '/dashboard');
};
return (
<>
<button onClick={handleEmployeeClick}>
Go to dashboard (employee only)
</button>
<Link href="/admin/dashboard" as="/dashboard">
Go to dashboard (admin only)
</Link>
</>
);
}
I just updated to next js v13 and I am get this error when I visit my website home page.
this file is causing the error
middleware.js
import { NextResponse } from 'next/server'
export default function Middleware(req) {
if (req.nextUrl.pathname !== req.nextUrl.pathname.toLowerCase()) {
const url = req.nextUrl.clone()
url.pathname = url.pathname.toLowerCase()
return NextResponse.redirect(url)
}
return NextResponse.next()
}
when i deleted this file my website worked and the error went away.
next js v13 made changes to middlewares: https://nextjs.org/blog/next-13#middleware-api-updates but i'm not sure how it affected my code
update: i just saw this in the next js v13 blog and made the changes but the error is still happening
Sending responses from Middleware currently requires the experimental.allowMiddlewareResponseBody configuration option inside next.config.js.
next.config.js.
const nextConfig = {
reactStrictMode: true,
experimental: {
allowMiddlewareResponseBody: true
},
}
Placing a humans.txt file into nextjs' /public folder works fine for a static file.
However I'd like to annotate the file with the date of the latest page build (when next build is called). So I created pages/humans.txt.tsx which renders a string that also contains the build time static date:
export default function Test({ buildTime }) {
return `Hello ${buildTime}`
}
export async function getStaticProps() {
return {
props: {
buildTime: new Date().toISOString()
}
}
}
I tried to customize pages/_document.js but even with everything stripped down (for testing) it still renders the doctype and one div with my text in it.
class MyDocument extends Document {
static async getInitialProps(ctx: DocumentContext) {
ctx.renderPage = (props) => {
return {
html: "text",
head: null,
}
}
// Run the parent `getInitialProps`, it now includes the custom `renderPage`
const initialProps = await Document.getInitialProps(ctx)
return initialProps
}
render() {
return <Main/>
}
}
Output:
<!DOCTYPE html><div id="__next">text</div>
Returning just string from my documents render instead of <Main/> still renders the doctype and also causes a warning, since render should return an Element.
So I am out of ideas and might resort to using a prebuild script in package.json prebuild: sed ./pages/humans.txt... to replace a marker in the file with the system date and pipe it to public/humans.txt.
Here is an interesting runtime alternative:
Rewriting /humans.txt to /api/humans
You can use the following rule:
// next.config.js
module.exports = {
async rewrites() {
return [
{
source: '/humans.txt',
destination: '/api/humans',
},
]
},
}
Check the Rewrites docs here
Writing /api/humans
Now you can use any response in your API. However, make sure you are caching it:
// /pages/api/humans.js
export default function handler(req, res) {
res.setHeader('Cache-control', 's-maxage=6000, stale-while-revalidate=30')
res.setHeader('Content-type', 'text/plain')
res.status(200).end('example')
}
Check the API routes docs here
In /pages I have [page].js and index.js.
[page].js generate needed Pages by the Value of "CustomPage". It's content comes from an Data-JSON-File.
It work like expected, as long as I start on the Homepage and use links inside of my Webpage.
For example I have 2 Pages for now: /impressum and /datenschutz.
So clicking the link "Impressum" open myDomain.com/impressum (and it work, BUT notice, there is no .html at the end).
BUT, if I refresh the page, or type myDomain.com/impressum directly in the addressbar of the browser, I got an not found error (from nginx-server, not from next!).
Second try
As I need a fully static page and I've added getStaticPath and getStaticProps in the file for testing purposes, so that "real" html-files will be created:
import { useRouter } from 'next/router';
import Index from './index';
import config from '../content/config.yml';
import CustomPage from '../src/components/CustomPage';
const RoutingPage = () => {
const { customPages } = config;
const router = useRouter();
const { page } = router.query;
const findMatches = (requestedPage) =>
customPages.find((customPage) => customPage.name === requestedPage) ||
false;
const customPageData = findMatches(page);
if (customPageData !== false) {
return <CustomPage pageContext={customPageData} />;
}
return page === 'index' ? (
<Index page={page} />
) : (
<p style={{ marginTop: '250px' }}>whats up {page}</p>
);
};
export async function getStaticPaths() {
return {
paths: [
{ params: { page: 'impressum' } },
{ params: { page: 'datenschutz' } },
],
fallback: false, // See the "fallback" section below
};
}
export async function getStaticProps({ params }) {
return { props: { page: params.page } };
}
export default RoutingPage;
This generates the single pages as real html-files:
But this lead me to the next issue:
I've implemented internal Links in the Webpage like this:
which still lead a user to myDomain.com/impressum, now additionally there is myDomain.com/impressum.html available. From SEO perspective, this are two different paths.
How do I get them unified, so that I have only one path - regardles of whether if I open it from within my Webpage, or enter it directly.
Workaround Idea (??)
Sure, I could everywhere use something like:
<Link href={`/${item.page}.html`}>
But this only work if the Page is exported and copied to the Server. For next dev and next start this won't work, because the .html-File don't exist.... and so I'll lost the "page preview" while working at the page.
So only Idea I have is to set an ENV-Variable for .env.development & .env.production and encapsulate the -Component from NEXT in a HOC.
In that HOC I could check if I'm currently in dev or prod and don't use .html for those links... otherwise add the .html to the link.
What YOU say about this. Do you have any other solution?
I don't know if it's state of the art, but as little workaround I did this:
I place the next/link-Component in a HOC and check if it's run on development or production (process.env.NODE_ENV):
import React from 'react';
import Link from 'next/link';
const LinkHoc = (props) => {
const { as, href, children } = props;
if (process.env.NODE_ENV === 'production') {
return (
<Link
{...props}
as={as ? `${as}.html` : ''}
href={href ? `${href}.html` : ''}
/>
);
}
return <Link {...props}>{children}</Link>;
};
export default LinkHoc;
With this workaround you get mydomain.com/impressum links in DEV and mydomain.com/impressum.html in production.
Only thing what to do at least is to rename the JSON-Files for the generated pages.
They are in /out/_next/data/XYZranadomString/.
They are named like impressum.json and you need to rename it to impressum.html.json to fix the 404 error on clientside for this files.
Would love to see a better Solution, so if you have any suggestions, please let me know!
Created a next.js full stack application. After production build when I run next start it returns 500 : internal server. I'm using environment varibles for hitting api.
env.development file
BASE_URL=http://localhost:3000
It was working fine in development
service.ts
import axios from 'axios';
const axiosDefaultConfig = {
baseURL: process.env.BASE_URL, // is this line reason for error?
headers: {
'Access-Control-Allow-Origin': '*'
}
};
const axio = axios.create(axiosDefaultConfig);
export class Steam {
static getGames = async () => {
return await axio.get('/api/getAppList');
};
}
Do you have a next.config.js file?
To add runtime configuration to your app open next.config.js and add the publicRuntimeConfig and serverRuntimeConfig configs:
module.exports = {
serverRuntimeConfig: {
// Will only be available on the server side
mySecret: 'secret',
secondSecret: process.env.SECOND_SECRET, // Pass through env variables
},
publicRuntimeConfig: {
// Will be available on both server and client
staticFolder: '/static',
},
}
To get access to the runtime configs in your app use next/config, like so:
import getConfig from 'next/config'
// Only holds serverRuntimeConfig and publicRuntimeConfig
const { serverRuntimeConfig, publicRuntimeConfig } = getConfig()
// Will only be available on the server-side
console.log(serverRuntimeConfig.mySecret)
// Will be available on both server-side and client-side
console.log(publicRuntimeConfig.staticFolder)
function MyImage() {
return (
<div>
<img src={`${publicRuntimeConfig.staticFolder}/logo.png`} alt="logo" />
</div>
)
}
export default MyImage
I hope this helps.
I dont think you have setup env.
You need to configure it for it to work. Try it without it and it should work fine!