npx create-next-app seo --use-yarn --example "https://github.com/vercel/next-learn/tree/master/basics/learn-starter"
Main Concept
Development stage: TypeScript and ESLint integration, Fast Refresh, ...
Production stage: Compiled, Bundled, Minified and Code Split
Compiled : JSX -> should be transformed to JS version that browser can understand
NestJS use SWC to replace Babel as a compiled tool.
Minifying: Removed unnessary code formatting and comments without changing the code's functionality
Bundled: Resolved the web of dependencies(packages) and merging the files(modules) into optimized bundled for browser, the goal is reducing the number of requests for files when user visit a web page
Code Splitting: Splitting the application's bundle into smaller chunks required by each entry point ( page URL ). The goal is reducing the initial time for the app - only loading the code required to run that page:
- Shared Code between pages
- Preloading Code
- Dynamic Imports
BuildTime: is giving name for a series of steps that transform application's code into production-optimized files to be deployed on server and consumed by users includes:
- HTML Files for static generated pages
- Javascript code for rendering pages on the server
- JavaScript code for making pages interactive on the client
- CSS Files
Runtime: refers to the period of time when your application runs in response to a user's request
Rendering: convert your reactjs code into HTML representation of your UI. Can be happened in
- Client Side - Client Side Rendering
- A head of time at build time - Static Side Generation ( Pre-Rendering )
- Server Side - Every request at runtime - Server Side Rendering ( Pre-Rendering )
Pre-Rendering
Client-Side-Rendering
Server-Side-Rendering
Static-Side-Generation
Can you have multiple rendering methods into a single next.js application?
Next.js pre-renders every page by default
Similar to CDNs, Edge servers are distributed to multiple locations around the world. But unlike CDNs, which store static content, some Edge servers can run code.
The main idea is creating a layout component which will wrap your page component. We can use the hierarchy file structure in nextjs to apply this pattern
// pages/_document.tsx
import { Html, Head, Main, NextScript } from "next/document";
export default function Document() {
return (
<Html lang="vi">
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
// pages/_app.tsx
import type { ReactElement, ReactNode } from "react";
import type { NextPage } from "next";
import type { AppProps } from "next/app";
import "../styles/global.scss";
export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
getLayout?: (page: ReactElement) => ReactNode;
};
type AppPropsWithLayout = AppProps & {
Component: NextPageWithLayout;
};
export default function MyApp({ Component, pageProps }: AppPropsWithLayout) {
// Use the layout defined at the page level, if available
const getLayout = Component.getLayout ?? ((page) => page);
return getLayout(<Component {...pageProps} />);
}
// components/layout-default.tsx
import Meta from "./partials/meta-head";
const Layout = ({ children }) => {
return (
<>
<Meta />
<main>{children}</main>
</>
);
};
export default Layout;
export async function getServerSideProps(context) {
return {
props: {}, // will be passed to the page component as props
}
}
export const getServerSideProps: GetServerSideProps<{
category: BlogCategoryModel;
}> = async (context) => {
const { query } = context;
const categorySlug = query.categorySlug;
if (categorySlug === "posts") {
return {
notFound: true,
};
}
const category: BlogCategoryModel = {
title: `Category [${categorySlug}]`,
posts: Array.from(new Array(6)).map((_, idx) => {
return {
title: `Post ${++idx}`,
content: "",
id: "1",
featureImage: "",
slug: "post-1",
};
}),
};
return {
props: { category },
};
};
import { GetStaticPaths, GetStaticProps } from "next";
import { useRouter } from "next/router";
import { PageModel } from "../business/models";
import Page from "../components/page";
export default (props: { page: PageModel }) => {
const router = useRouter();
const { page } = props;
if (router.isFallback) {
return <div>Loading...</div>;
}
//const { pageSlug } = router.query;
return <Page page={page}></Page>;
};
export const getStaticPaths: GetStaticPaths = async () => {
const paths = [{ params: { pageSlug: "lien-he" } }];
return {
paths,
// don't build static page at build time, keep it for the 1st request
// to reduce build time
fallback: true,
};
};
export const getStaticProps: GetStaticProps<{ page: PageModel }> = async ({
params,
}) => {
const pageSlug = (params["pageSlug"] as string) || "";
const page: PageModel = {
title: `Page with slug = ${pageSlug}`,
slug: pageSlug,
content: `Content of page with slug = ${pageSlug}`,
featureImage: "",
id: Math.random().toFixed(),
};
return {
props: {
page,
},
};
};
- .env.[NODE_ENV] to load environment variables
- Expose environment variables to the browser by prefixing with NEXT_PUBLIC_