We want to see what next.js is and what it is for, If you are creating an application that is on the client side and does not have SEO, you do not need Server Side Rendering (SSR), But if your application is public, you need to use SSR.
What next.js does is render the application to the server side and deliver html to you, which react.js does not do and only you have a div tag.
Components, Layouts & Styles
npx create-next-app@latest next-js --use-yarn
To install next.js, we use the command above In the next step, create a folder called cpmponents in your project To change the main page, we must change the pages folder and the index.js file.
export default function Home() {
return (
<div className={styles.container}>
<h1>Welcome to next!</h1>
</div>
)
}
Inside the pages folder, we create a file called about.js
import Head from 'next/head'
export default function About() {
return (
<div>
<h1>About page</h1>
</div>
)
}
If we enter localhost:3000/about in the browser, we will go to the about page. Now, inside the pages folder, create another folder called contact and put the index.js file inside it.
import Head from 'next/head'
export default function Contact() {
return (
<div>
<h1>Contact page</h1>
</div>
)
}
As you can see, there are two ways to create pages. Now we put a Layout.js file inside the components folder. Note that the first words should be capitalized.
import Head from 'next/head'
import styles from '../styles/Home.module.css'
export default function Layout(props) {
return (
<div className={styles.container}>
{props.children}
</div>
)
}
Now, to apply the layout to all pages, we apply this change to the pages folder and the _app.js file.
import Layout from '../components/Layout'
function MyApp({ Component, pageProps }) {
return (<Layout><Component {...pageProps} /></Layout>)
}
export default MyApp
Dynamic Routes
Create a file called Article List.js in the components folder
import articleStyles from '../styles/Article.module.css';
import ArticleItem from './ArticleItem';
const ArticleList = ({ articles }) => {
return (
<div className={articleStyles}>
{articles.map((article) => (
<ArticleItem key={article.id} article={article} />
))}
</div>
)
}
We also create a file called ArticleItem.js
import articleStyles from '../styles/Article.module.css';
import Link from 'next/link';
const ArticleList = ({ article }) => {
return (
<Link href="/article/[id]" as={`/article/${article.id}`}>
<a>
<h3>{article.title.toUppercase()} →</h3>
<p>{article.body}</p>
</a>
</Link>
)
}
And inside the index.js file of the pages folder
export default function Home({ articles }) {
return (
<div>
<h1>Welcome to next!</h1>
<ArticleList articles={articles} />
</div>
)
}
export const getStaticProps = async () => {
const response = await fetch(`https:jsonplaceholder.typicode.com/posts?_limit=6`)
const articles = await response.json()
return {
props: {
articles
}
}
}
Also, in the styles folder, there is a file called Article.module.css
.container {
padding: 0 2rem;
}
.main {
min-height: 100vh;
padding: 4rem 0;
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.footer {
display: flex;
flex: 1;
padding: 2rem 0;
border-top: 1px solid #eaeaea;
justify-content: center;
align-items: center;
}
.footer a {
display: flex;
justify-content: center;
align-items: center;
flex-grow: 1;
}
.title a {
color: #0070f3;
text-decoration: none;
}
.title a:hover,
.title a:focus,
.title a:active {
text-decoration: underline;
}
.title {
margin: 0;
line-height: 1.15;
font-size: 4rem;
}
.title,
.description {
text-align: center;
}
.description {
margin: 4rem 0;
line-height: 1.5;
font-size: 1.5rem;
}
.code {
background: #fafafa;
border-radius: 5px;
padding: 0.75rem;
font-size: 1.1rem;
font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
Bitstream Vera Sans Mono, Courier New, monospace;
}
.grid {
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
max-width: 800px;
}
.card {
margin: 1rem;
padding: 1.5rem;
text-align: left;
color: inherit;
text-decoration: none;
border: 1px solid #eaeaea;
border-radius: 10px;
transition: color 0.15s ease, border-color 0.15s ease;
max-width: 300px;
}
.card:hover,
.card:focus,
.card:active {
color: #0070f3;
border-color: #0070f3;
}
.card h2 {
margin: 0 0 1rem 0;
font-size: 1.5rem;
}
.card p {
margin: 0;
font-size: 1.25rem;
line-height: 1.5;
}
.logo {
height: 1em;
margin-left: 0.5rem;
}
@media (max-width: 600px) {
.grid {
width: 100%;
flex-direction: column;
}
}
API
Inside the api folder, create a folder named articles and a file named index.js
import { articles } from "../../../data";
export default function handler(req, res) {
res.status(200).json(articles)
}
We also create a file named [id].js inside the articles folder
import { articles } from "../../../data";
export default function handler({ query: { id } }, res) {
const filtered = articles.filter(article => article.id === id)
if (filtered.length > 0) {
res.status(200).json(filtered[0])
} else {
res.status(400).json({
error: `Article with id of ${id} not found!`
})
}
}
We also create a data.js file in the main project
export const articles = [
{
"userId": 1,
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
},
{
"userId": 1,
"id": 2,
"title": "qui est esse",
"body": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla"
},
{
"userId": 1,
"id": 3,
"title": "ea molestias quasi exercitationem repellat qui ipsa sit aut",
"body": "et iusto sed quo iure\nvoluptatem occaecati omnis eligendi aut ad\nvoluptatem doloribus vel accusantium quis pariatur\nmolestiae porro eius odio et labore et velit aut"
},
{
"userId": 1,
"id": 4,
"title": "eum et est occaecati",
"body": "ullam et saepe reiciendis voluptatem adipisci\nsit amet autem assumenda provident rerum culpa\nquis hic commodi nesciunt rem tenetur doloremque ipsam iure\nquis sunt voluptatem rerum illo velit"
}
]
Then in the index.js file in the pages folder
import Head from 'next/head'
import ArticleList from '../components/ArticleList'
import { server } from '../config'
export default function Home({ articles }) {
return (
<div>
<h1>Welcome to next!</h1>
<ArticleList articles={articles} />
</div>
)
}
// export const getStaticProps = async () => {
// const response = await fetch(`jsonplaceholder.typicode.com/posts?_limit=6`)
// const articles = await response.json()
// return {
// props: {
// articles
// }
// }
// }
export const getStaticProps = async () => {
const response = await fetch(`${server}/api/articles`)
const articles = await response.json()
return {
props: {
articles
}
}
}
Also, in the main project, we create a config folder and an index.js file inside it
const development = process.env.NODE_ENV !== 'production';
export const server = development ? 'localhost:3000' : ''
And finally, in the pages folder, we put a folder named article and inside it we put an [id] folder and inside this folder we put an index.js file.
import { useRouter } from "next/router";
import Link from 'next/link';
import { server } from "../../../config";
const article = ({ article }) => {
return (
<>
<div>
<h1>{article.title}</h1>
<p>{article.body}</p>
</div>
<Link href='/'>Go Back</Link>
</>
)
}
export const getStaticProps = async context => {
const response = await fetch(`${server}/api/articles/${context.params.id}`);
const article = await response.json();
return {
props: {
article
}
}
}
export const getStaticPaths = async context => {
const response = await fetch(`${server}/api/articles/posts`);
const articles = await response.json();
const ids = articles.map(article => article.id)
const paths = ids.map(id => ({ params: { id: id.toString() } }))
return {
paths: paths,
fallback: false
}
}
export default article;
You can also directly access these files through the link next-js