I have a nextjs static site. As you can see from the screenshot, the JS loads fast but then it waits for the styled-components to apply the CSS. The CSS is applied after the page loads.
Any ideas how I can either wait for the styled-components CSS to kick in?
UPDATE: ah, I forgot that this is a static app---that means nextjs will pre-render the output of the page, and then load the JS. So it looks like standard techniques to wait for JS to load should apply. HOWEVER...the gotcha is that the static HTML is auto-generated by nextjs, so need a way to do that thru nextjs.
Install Babel-plugin for styled-components => npm i -D babel-plugin-styled-components
Then Create pages/_document.js
import Document from 'next/document'
import { ServerStyleSheet } from 'styled-components'
export default class MyDocument extends Document {
static async getInitialProps(ctx) {
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()
}
}
If you want to use ThemeProvider from styled-components, add the ThemeProvider to pages/_app.js.
This is an example provided by Next.js
Related
I'm having trouble with implementing dynamic title with Next.js 13.
The Documents said, (https://beta.nextjs.org/docs/routing/pages-and-layouts#modifying-head)
Warning: Currently, the <Head> export does not re-render on client-side transition using next/link, only on initial render and reloads. To work around this for <title>, you can use a client component with useEffect that updates document.title. We plan to fix this in a future release.
And then I tried with useEffect to implement dynamic title,
'use client';
import { useEffect } from "react";;
import Head from 'next/head';
interface Props {
params: {slug : string},
searchParams: {id:string}
};
const getPost = async (id: string) => {
const res = await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`);
const posts = await res.json();
return posts;
};
const Page = ({params, searchParams}: Props) => {
useEffect(() => {
const setDocumentTitle = async () => {
const post = await getPost(params.slug);
document.title = post.title;
}
setDocumentTitle();
}, [params.slug]);
return (<>
<Head>
<title>Blog Post</title>
</Head>
<div>
<h1>Blog Post</h1>
<p>Slug: {params.slug}</p>
<p>Id: {searchParams.id}</p>
</div>
</>);
};
export default Page;
but it is not working. still title is not changed. Is there any way?
You can reproduce it here.codesandbox
The first few times seem to be working well, but the document title does not change if you continue to go back and click Go to Post1 (next/link).
I am trying to set up a graphql frontend application with nextjs13 but with the new folder structure as there ins't _app.tsx I am wondering how to setup
Before we use to wrap the entire app like this
<ApolloProvider client={client}>
<Component {...pageProps} />
</ApolloProvider>
Now how to set up that?
What you want to do now is leverage the layout functionnality of the new nextjs 13 routing.
Your _app.js now becomes the root layout, a layout.js file that must be placed at the base of the app directory.
As any file in this directory, they will be treated as server component by default, which will prevent your Provider to work. Therefore, you need to specify "use client" in this file and feed it the provider that will then be shared by the whole application.
Example code below:
"use client";
import { ApolloProvider } from "#apollo/client";
type RootLayoutProps = {
children: React.ReactNode;
};
const RootLayout = ({ children }: RootLayoutProps) => {
return (
<html>
<body>
<ApolloProvider>{children}</ApolloProvider>
</body>
</html>
);
};
export default RootLayout
The accepted answer doesn't work when I have attempted it like that. As mentioned in the comments this:
TypeError: Cannot read properties of undefined (reading 'Symbol(APOLLO_CONTEXT)')
Is the resulting error.
I found doing this works:
// /apollo/ApolloProvider.js
"use client";
import { ApolloProvider as Provider } from "#apollo/client";
import { client } from "./client";
export const ApolloProvider = ({ children }) => (
<Provider client={client}>{children}</Provider>
);
// /app/Layout.js
import { ApolloProvider } from "apollo";
const RootLayout = ({ children }) => (
<ApolloProvider>{children}</ApolloProvider>
);
export default RootLayout;
I'm trying to use tw-elements in a nodejs project. If I follow their documentation and just import tw-elements in my _app, I get this error:
ReferenceError: document is not defined
I found a stackoverflow response that said to put this at the start of the index.min.js file of tw-elements:
if (typeof window == "undefined")return;
I did and the error disappeared, but the library still won't work. Any ideas?
First, add Tailwind Elements using these NPM steps here.
Here is how to get it to work with Nextjs:
First step is to add this code to your _app.js file:
useEffect(() => {
const use = async () => {
(await import('tw-elements')).default;
};
use();
}, []);
Like this for example:
export default function App({ Component, pageProps }) {
useEffect(() => {
const use = async () => {
(await import('tw-elements')).default;
};
use();
}, []);
return (
Make sure you add import { useEffect } from "react"; to the top of _app.js.
It’s also important that you’re not importing Tailwind Elements anywhere else expect for the _app.js file.
Tailwind Elements should now be working!
I was facing the same issue. I followed Tyrell Curry's answer but It encountered type not found error because I was using typescript.
Unfortunately the type definitions were missing for tailwind-elements library.
I made a little change it the function so that type check have to be avoided by using as any.
useEffect(() => {
const use = async () => {
(await import("tw-elements" as any)).default;
};
use();
}, []);
I've searched Stack Overflow for similar questions but the answers either refer to old versions or aren't relevant to my situation.
I get the above error for the first component on the page which uses Styled Components. If I remove that component I get the error for the next one and so on, so I assume there's a problem with the hydration process and it's happening for every className.
I have a _document.tsx file which extends the Document class and has the following getInitialProps function
static async getInitialProps (ctx) {
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()
}
}
My hunch is that it is something to do with this code because before I upgraded to Typescript (and had to change my _app.tsx file) I didn't get this error, but I have no idea how to fix it.
Any help would really be appreciated.
Try installing babel-plugin-styled-components, then add a .babelrc file to the root of the project with the following configuration. This ensures that no mismatches in classes generation occur during rehydration.
{
"presets": ["next/babel"],
"plugins": [["styled-components", { "ssr": true }]]
}
From the babel-plugin-styled-components's documentation:
By adding a unique identifier to every styled component, this plugin avoids checksum mismatches due to different class generation on the client and on the server. If you do not use this plugin and try to server-side render styled-components React will complain with an HTML attribute mismatch warning during rehydration.
Next.js added support for Styled Components since v12.0.1. The only thing you need to do is modify the next.config.js file. It should look like the following.
/** #type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
// This is the property you need to add
compiler: {
// ssr and displayName are configured by default
styledComponents: true,
},
};
module.exports = nextConfig;
Source: Next.js Docs
I'm using a _document.js to secure that the nextjs will render the page using styled-components(this solve the break on reload), but i'm getting to errors.
I tried to solve it by upgrading next, react and also canary, but without sucess.
Bellow you can find the code and also the errors displayed.
import Document from 'next/document';
import { ServerStyleSheet } from 'styled-components';
export default class MyDocument extends Document {
static async getInitialProps(ctx) {
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();
}
}
}
Errors displayed:
./pages/_document.js
1:1 Error: next/document should not be imported outside of pages/_document.js. See https://nextjs.org/docs/messages/no-document-import-in-page. #next/next/no-document-import-in-page
12:27 Error: Component definition is missing display name react/display-name
info - Need to disable some ESLint rules? Learn more here: https://nextjs.org/docs/basic-features/eslint#disabling-rules
error Command failed with exit code 1.