Open icazemier opened 1 year ago
You can simply throw an error in your root/top-level layout.tsx
and launch your app with next start
(in dev you'll see the dev error view).
You can simply throw an error in your root/top-level
layout.tsx
and launch your app withnext start
(in dev you'll see the dev error view).
Dear @yannbolliger ,
I tried something like:
export default function RootLayout({ children }: { children: React.ReactNode }) {
// 🙏🏼
throw new Error('Whoops');
return (
<html lang="en" translate="no">
....
in : src/app/layout.tsx
(appDir:true)
or even with a setTimeout around it, but I just can't seem to simulate a situation where I see the global-error page popping up.
Could you give me an example?
This example fails for me too
Did you use the production build next start
?
Hi NextJS team,
I'm still struggling to "really" see the global-error page live in action.
Seems related to this (locked and limited conversation to collaborators
) issue: https://github.com/vercel/next.js/issues/46572
I can reproduce the fact global-error.tsx is (still) not working with the following steps to reproduce: (Pushed here: https://github.com/icazemier/nextjs-global-error-issue)
global-error.tsx
in /app
(as documented)"use client";
export default function GlobalError({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
return (
<html>
<body>
<h2>Something went wrong!</h2>
<button onClick={() => reset()}>Try again</button>
</body>
</html>
);
}
import "./globals.css";
import type { Metadata } from "next";
import { Inter } from "next/font/google";
const inter = Inter({ subsets: ["latin"] });
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
// Throw an error here!
setTimeout(() => {
throw new Error("Whoops");
}, 2000);
return (
<html lang="en">
<body className={inter.className}>{children}</body>
</html>
);
}
npm run build
npm start
Nothing in the front end either, but in the terminal it does show the error??
Obviously there is something wrong, because no global error is triggered.
Or I'm doing weird things (which often is the case .... 😅).
But my ticket/request still stands.
Not sure, but seeing several related issues, possibly... Tagging:
In the meantime I managed to get the error boundary working!
Partially because I didn't understand why the boundaries where not catching them... I read about it here: https://semaphoreci.com/blog/error-handling-layer-react#limitations-of-this-approach
Made a error catch provider to at least trigger the error boundary and showing itself
See: https://github.com/icazemier/nextjs-global-error-issue/tree/22da1965208eae721aafa3ca8e2ef80ad5cdc468
(Still not able to simulate the global-error.tsx)
Things you must understand first:
global-error.*
from CSS modules will not apply unless they are also imported in layout.*
and somehow used there too. This is because NextJS doesn't download the styles imported in global-error.*
to the client even though it uses the class names on the elements. (This could be a bug with Next.js 14.)
html
and body
tags should be present in global-error.*
. If you have custom attributes on them in layout.*
you may need to copy them. (This depends on whether the attributes are required to properly render the page.)useRouter
hook in global-error.*
. If you want to have a button that refreshes the page, you should use plain JavaScript: onClick={() => window.location.reload()}
.Create a global-error.tsx
file as per the docs and the above tips.
global-error.tsx
file:Skip this if you don't need a Reload button. But if you do, since we cannot use the useRouter
hook here, we depend on regular Javascript to do a full-page refresh:
<button onClick={() => window.location.reload()}>Reload page</button>
Note: The reset: () => void
parameter doesn't work for this. It just throws weird errors.
global-error.tsx
:Skip this if you don't have any CSS modules imported in global-error.tsx
. But if you do, for example:
import Styles from '@/styles/error.module.scss'
The cleanest and eaiest way is to use global styles instead of importing a module.
For example, you may have a global styles file in app
called globals.scss
and you are already importing it into layout.tsx
:
import './globals.scss'
You can create unique class names in this file with a globally unique prefix like ErrorPage__
then use them in global-error.tsx
.
The other hacky way if you want to import a module in global-error.tsx
is to add a dummy class in the module:
// A dummy element to force load the styles in layout.tsx
.dummy {
display: none;
}
And also import the module into layout.tsx
:
import ErrorStyles from '@/styles/error.module.scss'
And use it in a div
somewhere inside the body
tag (it doesn't matter where) in the layout.tsx
file:
<div className={ErrorStyles.dummy} />
The following code is for testing the global error page. It should not be deployed in an actual production deployment.
In order to test this, a component should be dynamically rendered at runtime. Any statically generated component will be pre-built at build time and therefore fail the build if an error is thrown.
One easy way I managed to test this is in a client component that conditionally adds something:
{condition && <Component />}
In the client component file, I add a new function:
const fakeError = () => {
console.log('Throwing a fake error!')
throw new Error('Whoops!')
}
Then I use this as a className attribute value. (It doesn't matter what you use it as. I chose className because it didn't throw a type error.)
{condition && (
<>
<Component />
<div className={fakeError()} />
</>
)}
Now I build and run the production build, using npm
or next
:
npm run build && npm run start
# OR:
next build && next start
Navigate on localhost
to the page which will fulfill the condition
. And viola! (This is my custom error page. Your own style will definitely be different.)
Tip: The error
object has some properties like name
, message
and digest
which you can use to display the error details on the global-error
page.
Good luck!
This is very very bad DX.
Apparently you can simulate it in dev by making a page route which has a client component which calls useRouter()
and has a button that calls router.push
to another route which is a server component that calls redirect
from next/navigation
to a third route. For some reason Next.js doesn't like this and throws a "Rendered more hooks than during the previous render" error. This triggers the global-error
page where the error could be displayed.
I have created this component. Include it on any page:
'use client';
import { useState, useEffect } from 'react';
export const BrokenModule = (props: unknown) => {
const [isBroken, setIsBroken] = useState(false);
useEffect(() => {
setTimeout(() => {
setIsBroken(true);
}, 2000);
}, []);
if (isBroken) {
const test = props.test.not;
return <p>{test}</p>;
}
return <p>Broken module</p>;
}
Guaranteed to get an error after 2 seconds.
@Antonio-Laguna unfortunately your method can only test error.tsx
but not global-error.tsx
. I noticed this because I have different contents for each of these files.
As of now, to test the global-error.js | tsx
file, the best known method is to do what I did and do a production build:
const fakeError = () => {
console.log('Throwing a fake error!')
throw new Error('Whoops!')
}
export default function FakeError({ condition }: { condition: boolean }) {
return condition && <div className={fakeError()} />
}
Somewhere in your code, like layout.tsx
:
<FakeError condition={true} />
As a reminder, you have to use build
to create a production build and serve it. In NextJS projects, this is usually done with npm run serve
. For any changes, you'll have to kill the server and rebuild a new production build.
I cant manage to catch the error... This is my app/error.tsx
import { useEffect } from 'react';
import { logException } from '@lib/utils';
export default function Error({
error
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
useEffect(() => {
console.error('error');
logException(error);
}, [error]);
return (
<div>
<h2>Something went wrong!Error</h2>
</div>
);
}
This is my app/global-error.tsx
import { useEffect } from 'react';
import { logException } from '@lib/utils';
export default function GlobalError({
error
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
useEffect(() => {
console.error('global error');
logException(error);
}, [error]);
return (
<html>
<body>
<h2>Something went wrong!Global</h2>
</body>
</html>
);
}
Set this ENV NODE_ENV production
in my docker file and start my server with docker - so it should be prod?
How I trigger the error in a client component
<Button
onClick={() => {
throw new Error('custom err');
}}
/>
I see the error thrown in console from client component button, but not in the error components.
"next": "14.0.4"
"react": "^18",
This was the default I initialised the project with
How I was able to view my global-error.tsx
// app/page.tsx
// Don't let next cache a functioning index.html at build time
export const dynamic = "force-dynamic";
export default async function Homepage() {
// ...
}
// app/layout.tsx
const RootLayout = async ({
children,
}: Readonly<{
children: React.ReactNode;
}>) => {
if (process.env.NEXT_PHASE !== PHASE_PRODUCTION_BUILD) {
// This will allow the build, but fail at runtime
throw new Error('Oh nooo!');
}
return <html />
}
I got here even though I managed to simulate it before with React Developer Tools and I answered the question in here: https://stackoverflow.com/a/78671438/12479141
Here is my solution :
What is the improvement or update you wish to see?
Would be nice, to somehow trigger an error which shows this page
Is there any context that might help us understand?
For testing purposes
Does the docs page already exist? Please link to it.
https://beta.nextjs.org/docs/api-reference/file-conventions/error#global-errorjs