900. More on Nextjs
Layout
- define the layout components
- ex: Header and footer
- define the styles for the layout components
- use the components in
_app.js
function Header() {
return <h2 className="header">Header - Lorem ipsum dolor.</h2>
}
export default Header;
.footer {
padding: 20px;
text-align: center;
background-color: tomato;
}
.header {
padding: 20px;
text-align: center;
background-color: teal;
}
import '../styles/globals.css'
import Header from "../components/header";
import Footer from "../components/footer";
import '../styles/layout.css';
function MyApp({Component, pageProps}) {
return <>
<Header/>
<Component {...pageProps} />
<Footer/>
</>
}
export default MyApp
Component Level Layouts
- what if you don't want the Header component to show up in certain pages
- How to
- define the desired layout in the specific page
- update
_app.js
to consider pages with different layout
import Footer from "../components/footer";
function About() {
return <h2>Lorem ipsum dolor sit amet?</h2>
}
export default About;
About.getLayout = function PageLayout(page) {
return (
<>
{page}
<Footer/>
</>
)
}
import '../styles/globals.css'
import Header from "../components/header";
import Footer from "../components/footer";
import '../styles/layout.css';
function MyApp({Component, pageProps}) {
if (Component.getLayout) {
return Component.getLayout(<Component {...pageProps} />)
}
return <>
<Header/>
<Component {...pageProps} />
<Footer/>
</>
}
export default MyApp
Single Layout
- we can use a single layout file to define our main website structure
function Layout({children}){
return <>
<Header/>
{children}
<Footer/>
</>
}
export default Layout;
import '../styles/globals.css'
import Footer from "../components/Layout";
import '../styles/layout.css';
function MyApp({Component, pageProps}) {
if (Component.getLayout) {
return Component.getLayout(<Component {...pageProps} />)
}
return <>
<Layout>
<Component {...pageProps} />
<Layout/>
</>
}
export default MyApp
Head Component
- built-in component for appending elements to the head of the page
import Head from "next/head";
function About() {
return <>
<Head>
<title>Amazon ecommerce</title>
<meta name={'description'} content={'One stop solution to all your needs'}/>
</Head>
<h2>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Corporis, reiciendis?</h2>
</>
}
export default About;
- you can define a global head section in
_app.js
- child components can override properties defined in parent component i.e
_app.js
- child components can override properties defined in parent component i.e
import '../styles/globals.css'
import Header from "../components/header";
import Footer from "../components/footer";
import '../styles/layout.css';
import Head from "next/head";
function MyApp({Component, pageProps}) {
if (Component.getLayout) {
return Component.getLayout(<Component {...pageProps} />)
}
return <>
<Head>
<title>Amazon gift card</title>
<meta name={'description'} content={'One stop solution to all your needs'}/>
<link rel="icon" href="/favicon.ico" />
</Head>
<Header/>
<Component {...pageProps} />
<Footer/>
</>
}
export default MyApp
Image Component
- why use image component over native
img
- generate & servers images with the right format, saving considerable size -> space -> loading time
- serve the images in
webp
format, which is the preferred format of web - lazy loads the image, which increases page load.
- only load images that are visible in the view-port.
- adds a placeholder while image is loading
- this preserves layout, good for clean , non-distributing layout
- to use place holder you need to use
static path
import Image from "next/image";
import image1 from '../public/images/1.jpg';
function Images() {
return (<>
//static image
<Image src={image1} placeholder={"blur"} alt={"static image"} height={400} width={400}/>
//dynamic images
<div style={{display: 'flex'}}>
{
['1', '2', '3', '4'].map(value => {
return <Image key={value} src={'/images/' + value + ".jpg"} alt="my images" height='300'
width='300'/>
})
}
< /div>
</>
)
}
export default Images;
- Common props to use with Image component, in addition to src, width, alt and height
- fill
- Components: <Image> | Next.js
- A boolean that causes the image to fill the parent element instead of setting width and height.
- The parent element must assign position: "relative", position: "fixed", or position: "absolute" style.
- use object-fit and object-position css property to change the default behaviour
- sizes
- A string that provides information about how wide the image will be at different breakpoints.
- The value of sizes will greatly affect performance for images using fill or which are styled to have a responsive size.
- Responsive images | web.dev
- placeholder
- Components: <Image> | Next.js
- A placeholder to use while the image is loading. Possible values are blur or empty. Defaults to empty.
- fill
<Image
src="/example.png"
placeholder="blur"
fill
sizes="(max-width: 768px) 100vw,
(max-width: 1200px) 50vw,
33vw"
/>
Remote Images
- remote-patterns
- When using an external URL, you must add it to remotePatterns in next.config.js.
module.exports = {
images: {
domains: ['assets.acme.com'],
},
}
Absolute Imports & Module Paths
- A better way to organize your imports
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@layout/*": [
"components/layout/*"
]
}
}
}
- based on this configuration,
baseUrl
specify all our paths start from the root directory- inside path we are aliasing
- @layout -> components/layout
//before
import '../styles/globals.css'
import Header from "../components/header";
import Footer from "../components/footer";
import '../styles/layout.css';
import Head from "next/head";
//after
import 'styles/globals.css'
import Header from "@layout/header";
import Footer from "@layout/footer";
import 'styles/layout.css';
import Head from "next/head";
Redirects
- Next.js redirects
- Redirects allow you to redirect an incoming request path to a different destination path.
- To use Redirects you can use the redirects key in
next.config.js
module.exports = {
reactStrictMode: true,
async redirects() {
return [
{
source: '/about',
destination: '/',
permanent: true,
},
]
},
}
- Redirects with different matching
- path matching
- wildcard path matching
- regex path matching
- header, cookie and query matching
- redirects with base-path support
- redirects with i18n support
Environment Variables
- How to define environment variables
- at project root create a file called
.env.local
- add the environment variables
- to access the variables use
process.env.[variable]
- at project root create a file called
touch .env.local
DB_USER=root
DB_PASSWORD=1234
NEXT_PUBLIC_ANALYTIC_KEY = 13RF2
export async function getServerSideProps(){
const db_user = process.env.DB_USER;
const dv_password = process.env.DB_PASSWORD;
}
export default function HomeComponent(){
console.log(process.env.NEXT_PUBLIC_ANALYTIC_KEY);
return <p>Home component </p>
}
- by default environment variables are server side only. meaning they won't be available in front-e end
- but if you want variables to be on front-end as well, prefix the variables with
NEXT_PUBLI_[variable_name]
- but be careful though not to include secret variables in the font-end end: i.e db_password, token_key
- but if you want variables to be on front-end as well, prefix the variables with
Prefetching
- Prefetch pages for faster client-side transitions. This method is only useful for navigations without next/link, as next/link takes care of prefetching pages automatically.
- Let's say you have a login page, and after a login, you redirect the user to the dashboard. For that case, we can prefetch the dashboard to make a faster transition, like in the following example:
import { useCallback, useEffect } from 'react'
import { useRouter } from 'next/router'
export default function Login() {
const router = useRouter()
const handleSubmit = useCallback((e) => {
e.preventDefault()
//const result = post("/login").data(data)
//id result ok... move user to next destination
router.push('/dashboard')
}, [])
useEffect(() => {
// Prefetch the dashboard page
router.prefetch('/dashboard')
}, [])
return (
<form onSubmit={handleSubmit}>
{/* Form fields */}
<button type="submit">Login</button>
</form>
)
}
Important Next.js Typescript Snippets
Rendering
import { GetStaticProps, GetStaticPaths, GetServerSideProps } from 'next'
export const getStaticProps: GetStaticProps = async (context) => {
// ...
}
export const getStaticPaths: GetStaticPaths = async () => {
// ...
}
export const getServerSideProps: GetServerSideProps = async (context) => {
// ...
}
API Request and Response
import type { NextApiRequest, NextApiResponse } from 'next'
type Data = {
name: string
}
export default (req: NextApiRequest, res: NextApiResponse<Data>) => {
res.status(200).json({ name: 'John Doe' })
}
config.json
// @ts-check
/**
* @type {import('next').NextConfig}
**/
const nextConfig = {
/* config options here */
}
module.exports = nextConfig