NextJS dynamic title - next.js

Have been googling forever and found a way to change the <title>. That way is this: https://github.com/zeit/next.js/tree/master/examples/layout-component
The main problem with this is that everytime someone refresh the site/change page the title goes from http://localhost:3000 to the actual Title (eg. About us) and I'm a bit afraid of how this is affecting the SEO.
What is the correct way of chaning the page title dynamically?
My layout file:
import Link from 'next/link'
import Head from './../node_modules/next/head'
export default function Layout({ children, title = 'Welcome to my website' }) {
return (
<div>
<Head>
<title>{title}</title>
</Head>
{children}
</div>
)
}

Check out next-seo and install it in your next.js application.
yarn add next-seo
# or
npm install --save next-seo
And it will handle the page title and the meta description for you magically.
import React from 'react';
import { NextSeo } from 'next-seo'; // then add the `NextSeo` at any `pages/` that you wish
export default () => (
<>
<NextSeo
title="About Us, or just any title that you wish"
description="Then with a short description here."
/>
<p>Simple Usage</p>
</>
);
I have implemented the same tactic on my own web app here.

Well for me this works,
Import <Head> from next,
import Head from 'next/head'
And in return statement,
<>
<Head>
<title>Page Title</title>
</Head>
<section>
<Your_JSX_CODE>
</section>
</>

If you need a dynamic title/description, for example for the route parameters case, you can do this. (Consider that the page name is [id].js)
import React from 'react'
import { NextSeo } from 'next-seo' //as proposed by #xun
// import Head from "next/head" //alternative solution
const Detail = ({id}) => {
const title = `My ${id} title.`
const description = `My ${id} description.`
return (
<>
<NextSeo
title={title}
description={description}
/>
<p>It works!</p>
</>
)}
export default Detail
And at the end of your file:
export async function getServerSideProps({query}) {
const { id } = query
return {
props: {
id
},
};
}
Good luck!

I reninstalled "next" and "next-routes" in my dependencies and now this works.

Related

Images not showing after adding const slider ... so confused

So i've been following a tutorial and trying to teach myself next.js with lazy loading slider and tailwindcss.. I am a beginner but i have made ecommerce sites and stuff to teach myself.. but this tutorial was going great until this!but Ive tried everything i know and googled it every which way to try to fix this issue!! My images were showing perfectly before i added the - const setCurrent up to the if !Array
if i delete that the images show back up... im so confused ive gone through everything - also the "Gallery" even disapeared once i added that section
and i put it all on github incase someone can look at it to help me?? https://github.com/Jessica19882/firebird
i have deleted the
const current up to the !Array part and images showed back up i have tried rewriting it as another post said but that didnt work ive checked and zoomed into the video tutorial to make sure everything was right and it is... i have checked all the other pages index.js app.js and stuff to make sure i had it just like his and it is!!
slider.js
import { SliderData } from './SliderData'
import React, { useState } from 'react'
import Image from 'next/image'
const Slider = ({ slides }) => {
const [current, setCurrent] = useState(0)
const length = slides?.length
const nextSlide = () => {
setCurrent(current === length - 1 ? 0 : current + 1)
}
const prevSlide = () => {
setCurrent(current === 0 ? length - 1 : current - 1)
}
if (!Array.isArray(slides) || slides.length <= 0) {
return null
}
return (
<div id='gallery'>
<h1>Gallery</h1>
<div>
{SliderData.map((slide, index) => {
return (
<div
key={index}
className={
index === current
? 'opacity-[1] ease-in duration-1000'
: 'opacity-0'
}>
<Image
src={slide.image}
alt='/'
width='1440'
height='600'
style={{ objectFit: 'cover' }}
/>
</div>
)
})}
</div>
</div>
)
}
export default Slider
index.js
import Head from 'next/head'
import Hero from '../components/Hero'
import Slider from '../components/Slider'
import SliderData from '../components/SliderData'
export default function Home() {
return (
<div>
<Head>
<title>Firebird Sounds</title>
<meta name='description' content='firebird sounds' />
<meta name='viewport' content='width=device-width, initial-scale=1' />
<link rel='icon' href='/favicon.ico' />
</Head>
<Hero
heading='Firebird Sounds - Audio & Video Distribution'
message='Audio and Video Distribution with a menu of
Services: Marketing, PR, Global rights management and creativity'
/>
<Slider Slides={SliderData} />
</div>
)
}
SliderData.js
export const SliderData = [
{
image:
'https://images.unsplash.com/photo-1466428996289-fb355538da1b?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxzZWFyY2h8MTl8fG11c2ljJTIwZGlzdHJpYnV0aW9ufGVufDB8fDB8fA%3D%3D&auto=format&fit=crop&w=500&q=60',
},
{
image: '/images/FirebirdSounds.jpg',
},
{
image:
'https://images.unsplash.com/photo-1460667262436-cf19894f4774?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxzZWFyY2h8NjZ8fG11c2ljJTIwZGlzdHJpYnV0aW9ufGVufDB8fDB8fA%3D%3D&auto=format&fit=crop&w=500&q=60',
},
{
image: '/images/guitar.jpg',
},
{
image: '/images/firebird.png',
},
]
app.js
import Navbar from '../components/Navbar'
import '../styles/globals.css'
export default function App({ Component, pageProps }) {
return (
<>
<Navbar />
<Component {...pageProps} />
</>
)
}
https://www.youtube.com/watch?fbclid=IwAR2jl5qYoIsaFZQ26MumbFcCYv5t3eYxcqVKgm4xAsxilhEkAzSNoUB0fzE&v=HVyct9EUNP8&feature=youtu.be
I figured it out I needed to put the
Slider slides={SliderData}/>
into _app.js not index.js or both but its working again!

How to change amp url inside head tag in next.js

I want to change the link tag URL ?amp=1 to ?amp inside the head in next.js and as it is auto-generated by next itself.
Also, I tried as per the below code but it's creating a duplication issue.
import Head from 'next/head'
import { useAmp } from 'next/amp'
import { useRouter } from 'next/router'
export default function MetaTag() {
const isAmp = useAmp()
const router = useRouter()
return (
<Head>
<link rel="amphtml" href={`${router?.asPath}?amp`} />
</Head>
)
}
Please refer to the below image to get more ideas:

NextJS - ReactDOMServer does not yet support Suspense

I'm currently trying to incorporate a loader component to a site built with NextJS. I would like to use Suspense to show a loading screen may it be after refreshing the page or changing routes.
This is how my code goes:
import Head from 'next/head'
import { Loader } from '../components/loader'
const { Suspense } = require('React')
function MyApp({ Component, pageProps }) {
return (
<>
<Suspense fallback={<Loader />}>
<Head>
.... some codes such as meta tags, title tags ....
</Head>
<Component {...pageProps} />;
</Suspense>
</>
)
}
My problem is I get an error that says ReactDOMServer does not yet support Suspense. but I would like to use Suspense to enable a loading screen on my page. Much like this website
You can use React 18 features like suspense in Next.js Advanced Features. Obviously it's still experimental and might cause issues with you application.
npm install next#latest react#rc react-dom#rc
To enable, use the experimental flag concurrentFeatures: true
// next.config.js
module.exports = {
experimental: {
concurrentFeatures: true,
},
}
Once enabled, you can use Suspense and SSR streaming for all pages.
import dynamic from 'next/dynamic'
import { lazy, Suspense } from 'react'
import Content from '../components/content'
// These two ways are identical:
const Profile = dynamic(() => import('./profile'), { suspense: true })
const Footer = lazy(() => import('./footer'))
export default function Home() {
return (
<div>
<Suspense fallback={<Spinner />}>
{/* A component that uses Suspense-based */}
<Content />
</Suspense>
<Suspense fallback={<Spinner />}>
<Profile />
</Suspense>
<Suspense fallback={<Spinner />}>
<Footer />
</Suspense>
</div>
)
}
I had a similar issue. I ended up simulating the Suspense with a combination of setState & componentDidMount
render(){
return this.state.browser ? <Component/> : <Placeholder/>
}
componentDidMount(){
this.setState({browser: true})
}
I hope it helps.

Set up Storybook to work with Next.js's Link tag

I'm trying to set up Storybook for a Next.js project. I have a component that render the Link tag from Next.js. My problem is that when I load this component, Storybook throws the following error:
Cannot read property 'pageLoader' of null
at Link.handleRef
What does one have to do to get Storybook working with Next.js Routing, specifically rendering the Link tag?
Update: Code that causes the error:
// button-component.js
import Link from 'next/link.js';
import t from 'prop-types';
import React from 'react';
function Button({ as, children, href, ...props }) {
const isExternal = href && href.startsWith('http');
const a = (
<a href={href} {...props}>
{children}
</a>
);
if (href) {
return isExternal ? (
a
) : (
<Link href={href} as={as}>
{a}
</Link>
);
}
return (
<button type="button" {...props}>
{children}
</button>
);
}
Button.propTypes = {
as: t.string,
children: t.node,
href: t.string,
};
export default React.memo(Button);
// button.stories.js
import React from 'react';
import Button from './button-component';
export default {
title: 'Button',
};
export const standardButton = () => <Button>Standard Button</Button>;
export const internalLink = () => <Button href='/buy'>
Internal Link
</Button>;
export const externalLink = () => (
<Button target="_blank" href="https://www.hopin.to">
External Link
</Button>
);
I found an issue reported about this on Next.js's github: https://github.com/zeit/next.js/issues/9951
It was reported only 5 days ago, so you could be having the same issue. The resolution is to upgrade to nextjs v9.1.8-canary.6. Reading more about this and looking at the source code, this is likely your problem. Also, there are more recent canary builds of nextjs, if you want to try something newer.
If that doesn't resolve it, my other guess is that you're getting errors because you're using Link outside of a Next.js page. Next.js may include dependencies for pages, behind the scenes. Link may rely on those dependencies and is throwing an error when they aren't found. If you want to test your components outside of Next.js pages, you could create a custom Link component that tests whether you're in Next.js and only renders Link if you are. For example:
import Link from 'next/link'
import Router from 'next/router'
const CustomLink = ({children, ...otherProps}) => {
const isPage = () => {
// if we're in a next.js route, then Router.router will be set
return Boolean(Router.router)
}
return isPage()
? (<Link {...otherProps}>{children}</Link>)
: children
}
Then use CustomLink instead of Link.
Another solution I found works similar as with next/image. To your .storybook/preview.js add following:
import Link from "next/link";
Object.defineProperty(Link, "default", {
configurable: true,
value: (props) => <a {...props} />,
});

Dynamically load .css based on condition in reactJS application

I have a reactJS application that I want to make available to multiple clients. Each clients has unique color schemes. I need to be able to import the .css file that corresponds to the specific client.
For example, if client 1 logs into the application, I want to import client1.css. if client 2 logs into the application, I want to import client2.css. I will know the client number once I have validated the login information.
The application contains multiple .js files. Every .js file contains the following at the top of the file
import React from 'react';
import { Redirect } from 'react-router-dom';
import {mqRequest} from '../functions/commonFunctions.js';
import '../styles/app.css';
Is there a way to import .css files dynamically for this scenario as opposed to specifying the .css file in the above import statement?
Thank you
Easy - i've delt with similar before.
componentWillMount() {
if(this.props.css1 === true) {
require('style1.css');
} else {
require('style2.css');
}
}
Consider using a cssInJs solution. Popular libraries are: emotion and styled-components but there are others as well.
I generally recommend a cssInJs solution, but for what you are trying to do it is especially useful.
In Emotion for example they have a tool specifically build for this purpose - the contextTheme.
What cssInJs basically means is that instead of using different static css files, use all the power of Javascript, to generate the needed css rules from your javascript code.
A bit late to the party, I want to expand on #Harmenx answer.
require works in development environments only, once it goes to production you're likely to get errors or not see the css file at all. Here are some options if you, or others, encounter this:
Option 1: Using css modules, assign a variable of styles with the response from the import based on the condition.
let styles;
if(this.props.css1 === true) {
//require('style1.css');
import("./style1.module.css").then((res) => { styles = res;});
} else {
//require('style2.css');
import("./style2.module.css").then((res) => { styles = res;});
}
...
<div className={styles.divClass}>...</div>
...
Option 2: using Suspend and lazy load from react
// STEP 1: create components for each stylesheet
// styles1.js
import React from "react";
import "./styles1.css";
export const Style1Variables = (React.FC = () => <></>);
export default Style1Variables ;
// styles2.js
import React from "react";
import "./styles2.css";
export const Style2Variables = (React.FC = () => <></>);
export default Style2Variables ;
// STEP 2: setup your conditional rendering component
import React, {lazy, Suspense} from "react";
const Styles1= lazy(() => import("./styles1"));
const Styles2= lazy(() => import("./styles2"));
export const ThemeSelector = ({ children }) => {
return (
<>
<Suspense fallback={null} />}>
{isClient1() ? <Styles1 /> : <Styles2/>}
</Suspense>
{children}
</>
);
};
// STEP 3: Wrap your app
ReactDOM.render(
<ThemeSelector>
<App />
</ThemeSelector>,
document.getElementById('root')
);
Option 3: Use React Helm which will include a link to the stylesheet in the header based on a conditional
class App extends Component {
render() {
<>
<Helmet>
<link
type="text/css"
rel="stylesheet"
href={isClient1() ? "./styles1.css" : "./styles2.css"}
/>
</Helmet>
...
</>
}
}
Personally, I like option 2 because you can set a variable whichClientIsThis() then modify the code to:
import React, {lazy, Suspense} from "react";
let clientID = whichClientIsThis();
const Styles= lazy(() => import("./`${clientID}`.css")); // change your variable filenames to match the client id.
export const ThemeSelector = ({ children }) => {
return (
<>
<Suspense fallback={null} />}>
<Styles />
</Suspense>
{children}
</>
);
};
ReactDOM.render(
<ThemeSelector>
<App />
</ThemeSelector>,
document.getElementById('root')
);
This way you don't need any conditionals. I'd still recommend lazy loading and suspending so the app has time to get the id and make the "decision" on which stylesheet to bring in.

Resources