problem in my header with nextjs i18n in 404 page - reactjs

I have a problem with nextjs i18n
I have 4 languages and when I change them everything is correct but on page 404 my header and content are wrong
anyone can help me?
_app.js
import { appWithTranslation } from "next-i18next";
import Header from "../components/Header";
import "../styles/globals.css";
function MyApp({ Component, pageProps }) {
let index = 2;
return (
<div dir={index === 1 ? 'rtl' : 'ltr'}>
<Header />
<Component {...pageProps} />
</div>
);
}
export default appWithTranslation(MyApp);
and index.js
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
import { useTranslation } from "next-i18next";
import MainPage from "../components/MainPage";
export default function Home() {
return (
<main>
<MainPage />
</main>
);
}
export async function getStaticProps({ locale }) {
return {
props: {
...(await serverSideTranslations(locale, [
"common",
"newsletter", // Add newsletter translations
"built-in-demo", // Add the built-in demo translations
"navbar"
])),
// Will be passed to the page component as props
},
};
}
and app structure is :
nextjsapp
.next
components
locales
node_modules
pages
404.js
index.jsx

Related

Next JS with react-intl creates an ancestry issue

I'm trying to setup NEXT Js with react-intl.
I've added to my _app.tsx the setup for react-intl as follows:
import { IntlProvider } from 'react-intl';
import English from "../../lang/compiled-locales/en-US.json"
import French from "../../lang/compiled-locales/fr.json"
import configs from '../../configs';
// https://itnext.io/next-js-react-intl-add-internationalisation-to-your-nextjs-app-4f933104b1f7
const MyApp = ({ Component, pageProps }: AppProps) => {
const { locale } = useRouter()
const messages = useMemo(() => {
switch (locale) {
case "fr":
return French
default:
return English
}
}, [locale])
return <IntlProvider
locale={locale || configs.languages.default}
messages={messages}>
<Component {...pageProps} />
</IntlProvider>
}
export default MyApp
When I ran it, it worked like a charm, but when I do a page reload, I get the following error:
Server Error
Error: [React Intl] Could not find required `intl` object. <IntlProvider> needs to exist in the component ancestry.
This error happened while generating the page. Any console logs will be displayed in the terminal window.
I'm guessing it's something to do with SSR but no idea how to fix. Thank you in advance for your help.
Here is the sample usage in my pages/index.tsx
import type { NextPage } from 'next'
import Head from 'next/head'
import { FormattedMessage } from 'react-intl'
import Header from '../components/Header'
const Home: NextPage = () => {
return <div className="">
<Head>
<title><FormattedMessage defaultMessage="FlipFlip - Print your videos for real!" id="landing.seo.title" /></title>
</Head>
<Header />
</div>
}
export default Home

Extensible layout components in nextjs

I am creating an application in nextjs. I understand that I can generate a layout like the example given in the docs
import Navbar from './navbar'
import Footer from './footer'
export default function Layout({ children }) {
return (
<>
<Navbar />
<main>{children}</main>
<Footer />
</>
)
}
However, I would like to change the contents of the Navbar on a per page basis.
export default function ListPage() {
return {
/** I want to add a secondary nav to the Navbar on this page */
<>
<Navbar><MySecondaryNav></Navbar>
....
</>
}
}
export default function ClientPage() {
return {
/** I want to add a a different secondary nav to the Navbar on this page */
<>
<Navbar><ClientNavbar></Navbar>
....
</>
}
}
I also need the markup to be rendered server-side. Is it possible to achieve this in nextjs?
Yes, you can!
You could use useRouter and then depending on the path render the correct component in NavBar
import { useRouter } from "next/router";
const router = useRouter();
now you should have access to router.pathname, depending on the pathname just render the correct component inside NavBar
#Edit - SSR solution
If you want this SSR, then this is how you do it
in your _app.tsx/jsx you can use getServerSideProps and then get directly resolvedUrl
export default function App({ Component, pageProps }) {
return (
<Layout {...pageProps}>
<Component {...pageProps} />
</Layout>
);
}
export async function getServerSideProps({{req, res, resolvedUrl}) {
return { props: { resolvedUrl } }
}
At this point you should be receiving resolvedUrl and based on that render the required component.
If this wont work for you we might need to setup a codesandbox

How to provide context from contextApi for a specific set of routes in nextjs and preserve state with routing through linking?

I am using contextApi with nextjs and I'm having some trouble when providing a context just for certain routes. I am able to make the context available for just a few routes, but when I transition from one to the other through linking, I end up losing the state of my application.
I have three files inside my pages folder:
index.tsx,
Dashboard/index.tsx and
SignIn/index.tsx.
If I import the provider inside the files Dashboard/index.tsx and SignIn/index.tsx and go from one page to the other by pressing a Link component from next/link, the whole state is set back to the initial state.
The content of the Dashboard/index.tsx file
import React from 'react';
import Dashboard from '../../app/views/Dashboard';
import { AuthProvider } from '../../contexts/auth';
const Index: React.FC = () => (
<AuthProvider>
<Dashboard />
</AuthProvider>
);
export default Index;
This is the contend of the SignIn/index.tsx file:
import React from 'react';
import SignIn from '../../app/views/SignIn';
import { AuthProvider } from '../../contexts/auth';
const Index: React.FC = () => (
<AuthProvider>
<SignIn />
</AuthProvider>
);
export default Index;
The views folder is where I create the components that will be rendered.
The content of the file views/SignIn/index.tsx is:
import React, { useContext } from 'react';
import Link from 'next/link';
import { AuthContext } from '../../../contexts/auth';
const SignIn: React.FC = () => {
const { signed, signIn } = useContext(AuthContext);
async function handleSignIn() {
signIn();
}
return (
<div>
<Link href="Dashboard">Go back to Dashboard</Link>
<button onClick={handleSignIn}>Click me</button>
</div>
);
};
export default SignIn;
And the content of the file views/Dashboard/index.tsx is:
import React, { useContext } from 'react';
import Link from 'next/link';
import { AuthContext } from '../../../contexts/auth';
const Dashboard: React.FC = () => {
const { signed, signIn } = useContext(AuthContext);
async function handleSignIn() {
signIn();
}
return (
<div>
<Link href="SignIn">Go back to sign in page</Link>
<button onClick={handleSignIn}>Click me</button>
</div>
);
};
export default Dashboard;
I am able to access the context inside both /Dashboard and /SignIn, but when I press the link, the state comes back to the initial one. I figured out that the whole provider is rerenderized and therefore the new state becomes the initial state, but I wasn't able to go around this issue in a "best practices manner".
If I put the provider inside _app.tsx, I can maintain the state when transitioning between pages, but I end up providing this state to the / route as well, which I am trying to avoid.
I was able to go around this by doing the following, but it really does not seem to be the best solution for me.
I removed the Providers from Pages/SignIn/index.tsx and Pages/Dashboard/index.tsx and used the following snippet for the _app.tsx file:
import React from 'react';
import { AppProps } from 'next/app';
import { useRouter } from 'next/router';
import { AuthProvider } from '../contexts/auth';
const App: React.FC<AppProps> = ({ Component, pageProps }) => {
const router = useRouter();
const AuthProviderRoutes = ['/SignIn', '/Dashboard'];
return (
<>
{AuthProviderRoutes.includes(router.pathname) ? (
<AuthProvider>
<Component {...pageProps} />
</AuthProvider>
) : <Component {...pageProps} />}
</>
);
};
export default App;
Does anyone have a better solution?

How can I use Keycloak in Next.js?

I'm trying to authenticate in Keycloak in Next.js using #react-keycloak/nextjs but once I pass the login it keeps sending me back to the Keycloak server and returning to the main page until it gives me an error because my token key is expired.
This is my _app.js:
import { ApolloProvider } from '#apollo/react-hooks'
import CssBaseline from '#material-ui/core/CssBaseline'
import { ThemeProvider } from '#material-ui/core/styles'
import { Persistors, SSRKeycloakProvider } from '#react-keycloak/nextjs'
import Layout from 'components/layout/Layout'
import { useApollo } from 'lib/apolloClient'
import KeycloakLoading from 'components/KeycloakLoading'
import {
keycloakCfg,
keycloakProviderInitConfig,
onKeycloakLogout,
onKeycloakTokens,
} from 'lib/keycloak'
import Head from 'next/head'
import PropTypes from 'prop-types'
import React from 'react'
import theme from 'styles/theme'
import 'leaflet/dist/leaflet.css'
import 'react-leaflet-markercluster/dist/styles.min.css'
import 'styles/styles.css'
import 'styles/animations.css'
import '../styles/globals.css'
// export function reportWebVitals(metric) {
// // These metrics can be sent to any analytics service
// console.log(metric)
// }
export default function MyApp(props) {
const { Component, pageProps, cookies } = props
const apolloClient = useApollo(pageProps.initialApolloState)
React.useEffect(() => {
// Remove the server-side injected CSS.
const jssStyles = document.querySelector('#jss-server-side')
if (jssStyles) {
jssStyles.parentElement.removeChild(jssStyles)
}
}, [])
return (
<React.Fragment>
<Head>
<title>My page</title>
<meta
name="viewport"
content="minimum-scale=1, initial-scale=1, width=device-width"
/>
</Head>
<ThemeProvider theme={theme}>
<CssBaseline />
<SSRKeycloakProvider
keycloakConfig={keycloakCfg}
persistor={Persistors.Cookies(cookies)}
initConfig={keycloakProviderInitConfig}
onTokens={onKeycloakTokens}
onAuthLogout={onKeycloakLogout}
LoadingComponent={
<React.Fragment>
<KeycloakLoading />
</React.Fragment>
}
>
<ApolloProvider client={apolloClient}>
<Layout>
<Component {...pageProps} />
</Layout>
</ApolloProvider>
</SSRKeycloakProvider>
</ThemeProvider>
</React.Fragment>
)
}
MyApp.propTypes = {
Component: PropTypes.elementType.isRequired,
pageProps: PropTypes.object.isRequired,
cookies: PropTypes.any,
}
And this is my Keycloak configuration
export const keycloakCfg = {
realm: APP_CONSTANTS.KEYCLOAK_REALM,
url: `${APP_CONSTANTS.KEYCLOAK_HOST}/auth`,
clientId: APP_CONSTANTS.KEYCLOAK_CLIENT_ID,
}
export const keycloakProviderInitConfig = {
onLoad: 'login-required',
}
If anyone could point out what is wrong or alternatively tell me another way to use Keycloak in next.
React-keycloak/nextjs is deprecated in favor of react-keycloak/ssr. I followed the examples here: https://www.npmjs.com/package/#react-keycloak/ssr and was able to fully authenticate without issue.
I'm not sure what is causing your looping issues but it doesn't look like you are using the required cookie parser or passing them into the props via getInitialProps so that may be a good place to start.
An alternative would be:
Next-Auth has implemented a keycloak provider which would make your life much easier protecting both Frontend and Backend. Just specify keycloak options/configs in _app.js and you all set.
import {signOut, getSession, useSession } from 'next-auth/react';
export default function Home() {
let session = useSession();
console.log(session);
return (
<div >
<button onClick={() => {
console.log(session);
signOut();
console.log(session);
}}> Sign Out </button>
<h1>H1 test</h1>
</div>
)
}
export const getServerSideProps = async (ctx) => {
const backendSession = await getSession(ctx)
console.log(backendSession);
return {
props: {
}
}
}

Next.js 9+ FOUC (Flash or Unstyled Content) with Styled Components

Have spent a couple days on this issue sourcing solutions and ideas from most of SA and Reddit however to no avail..
Upon load both in production and local every load presents the whole html without any styling before then being injected into DOM..
Currently this is my projects key files:
_document.js
import { ServerStyleSheet } from "styled-components";
import Document, { Main, NextScript } from "next/document";
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();
}
}
render() {
return (
<html lang="en">
<Head>
<title>namesjames</title>
<link rel="icon" href="/favicon.ico" />
<script src="/static/chrome-fix.js" />
<link href="/above-the-fold.css" />
</Head>
<body>
<script src="/noflash.js" />
<Main />
<NextScript />
<script> </script>
</body>
</html>
);
}
}
_app.js
/* eslint-disable class-methods-use-this */
import App from "next/app";
import React from "react";
import { ThemeProvider } from "styled-components";
import { ParallaxProvider } from 'react-scroll-parallax';
import Header from "../components/Header";
import theme from "../theme";
import GlobalStyles from "../GlobalStyles";
import DarkModeToggle from '../components/toggle/toggleMode';
import Footer from '../components/Footer'
import LazyLoad from 'react-lazy-load';
import Router from 'next/router';
import styled from 'styled-components'
// import '../style.css'
const Loaded = styled.div`
opacity: ${(props) => props.loaded ? "1" : "0"};
`
export default class MyApp extends App {
state = { isLoading: false, loaded: false }
componentDidMount() {
// Logging to prove _app.js only mounts once,
// but initializing router events here will also accomplishes
// goal of setting state on route change
console.log('MOUNT');
this.setState({loaded: true})
Router.events.on('routeChangeStart', () => {
this.setState({ isLoading: true });
console.log('loading is true, routechangeStart')
});
Router.events.on('routeChangeComplete', () => {
this.setState({ isLoading: false });
console.log('loading is false, routeChangeComplete')
});
Router.events.on('routeChangeError', () => {
this.setState({ isLoading: false });
console.log('loading is false, routeChangeError')
});
}
render(): JSX.Element {
const { isLoading } = this.state;
const { Component, pageProps, router, loaded } = this.props;
return (
<Loaded loaded={this.state.loaded}>
<ThemeProvider theme={theme}>
<GlobalStyles />
<ParallaxProvider>
<Header />
{isLoading && 'STRING OR LOADING COMPONENT HERE...'}
<Component {...pageProps} key={router.route} />
<LazyLoad offsetVertical={500}>
<Footer />
</LazyLoad>
</ParallaxProvider>
<DarkModeToggle />
</ThemeProvider>
</Loaded>
);
}
}
index.js
import { color } from "styled-system";
import { OffWhite } from "../util/tokens";
import Hero from "../components/Hero";
import Banner from '../components/Banner'
import TitleText from '../components/TitleText'
import HomeTitleCopyScene from '../components/HomeTitleCopyScene'
import TwoCards from '../components/TwoCards'
import LazyLoad from 'react-lazy-load';
function Home(): JSX.Element {
return (
<div className="container">
<Banner />
<HomeTitleCopyScene />
<LazyLoad offsetVertical={1000}>
<TwoCards />
</LazyLoad>
</div>
);
}
export default Home;
As some might see I have tried multiple implementations and am just a bit confused as to what it could be at this stage..
Any help appreciated and I can provide more information upon request.. Many thanks
I found two solutions which helped -->
Was to hard style opacity:0 into the JSX and then upon styling injecting into DOM applying opacity: 1 !important onto any of the components displayed..
<section className="cards-block" style={{opacity:0}}>
Whilst this was effective this morning I discovered at some point during my development I had incorrectly imported Head from next/head and used this in my _document.js rather than using the correct Head from next/documents..
// import Head from "next/head"; --> incorrect
import { ServerStyleSheet } from "styled-components";
import Document, { Head, Main, NextScript } from "next/document";
Ergo -> a correctly rendering and injected element with no FOUC
Hope this helps someone out there
I've found a workaround for my small portfolio project:
just included this inline css to a <head> of my custom _document.js:
{<style dangerouslySetInnerHTML={{__html: `
html {background: #333}
body #__next div {visibility: hidden}
body.loaded #__next div {visibility: visible}
`}}></style>}
The "loaded" class is added to the body in _app.js:
if (process.browser) {
document.body.classList.add("loaded")
}
I'm hot sure if it's a good solution, any advice would be appreciated :)

Resources