Search by

    Terakhir diperbaharui: Jan 21, 2021

    Next.js

    Apa itu Next.js?

    Next.js adalah framework React yang dibuat untuk mengatasi masalah client-side rendering yang dimiliki React.

    Sebuah halaman website yang dibuat menggunakan React ‘terasa ringan’ karena tampilan website sangat interaktif.

    Selain itu, saat data berubah, React dengan efisien akan mengupdate bagian dari halaman website yang memang perlu diupdate tanpa perlu reload satu halaman penuh.

    Untuk mendapatkan itu semua, client harus load semua file JavaScript sebelum konten halaman ditampilkan. Jika file JS cukup besar maka waktu yang dibutuhkan untuk load pertama kali juga menjadi lebih lama.

    Masalah lain dari client-side rendering adalah SEO, ada kemungkinan web crawler berusaha mengindex halaman yang belum selesai dirender sepenuhnya (karena waktu load yang lama). Dan menganggap web tersebut blank.

    Kedua masalah di atas dapat diselesaikan dengan teknik pre-rendering. Yaitu halaman HTML dan file JavaScript di generate sebelum dikirim ke client.

    Ada 2 bentuk pre-rendering yaitu Server Side Rendering (SSR) dan Static Site Generator (SSG).

    Server Side Rendering(SSR)

    Sesuai namanya proses render terjadi di server dan bukan di client, setiap request ke server akan direspon dengan sebuah halaman HTML.

    Static Site Generator (SSG)

    Setiap request ke server akan direspon dengan halaman HTML yang sudah selesai digenerate pada saat proses build. Teknik cache menggunakan CDN dapat diterapkan untuk mempersingkat waktu akses.

    Dan Next.js dapat melakukan keduanya.

    Features

    Selain pre-rendering, Next.js juga memiliki beberapa fitur untuk memudahkan kita dalam membuat aplikasi React:

    • Page-based routing system, alamat dari halaman adalah nama file dari halaman tersebut
    • Code splitting, code JavaScript dipecah menjadi chunk agar waktu loading halaman menjadi lebih singkat
    • Client-side routing, routing juga bisa dilakukan di sisi client
    • Fast refresh support, mirip dengan hot reloading
    • Built-In CSS, support CSS Module, SAAS dan CSS-in-JS
    • Automatic Image Optimization, tersedia pada Next.js versi 10 keatas
    • API Routes, membuat API tanpa library tambahan

    Pada bagian ini kita akan bahas konsep-konsep yang ada di Next.js dengan cara membuat sebuah blog sebelum kita terapkan pada aplikasi DinoTes.

    Step by step

    Persiapan

    Pastikan tool berikut ini sudah terinstall:

    • Visual Studio Code
    • Terminal
    • Nodejs + NPM/yarn

    Install Next JS

    Untuk membuat sebuah aplikasi Next.js kita bisa gunakan package create-next-app, mirip dengan create-react-app.

    Jalankan perintah berikut ini di terminal:

    1$ npx create-next-app my-blog

    Kemudian pindah folder dan jalankan aplikasi

    1$ cd my-blog
    2$ yarn dev

    Akses localhost:3000, jika tidak ada masalah kita akan dapatkan tampilan seperti ini:

    nextjs first install

    Setelah install ,yang akan kita lakukan adalah membuat halaman baru dan navigasi antar halaman.

    Sistem routing pada Next.js adalah page-based, artinya setiap halaman memiliki alamat url (slug) sesuai dengan nama file dari halaman tersebut.

    Contoh:

    • Halaman pages/about.js dapat diakses dengan alamat url /about
    • Halaman pages/posts/my-post.js dapat diakses dengan alamat url /posts/my-post
    • Hal ini berbeda dengan aplikasi React dimana untuk membuat sistem routing kita harus gunakan package tambahan seperti react-router-dom.

    Membuat halaman baru

    Buat sebuah folder dengan nama posts kemudian buat sebuah file baru dengan nama first-post.js.

    Susunan Folder:

    nextjs folder structure

    Salin code berikut ini di dalam file first-post.js.

    first-post.js

    1export default function FirstPost() {
    2 return (
    3 <>
    4 <h1>First Post</h1>
    5 <p>
    6 Lorem Ipsum is simply dummy text of the printing and typesettingindustry. Lorem Ipsum has been the industry's
    7 standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make
    8 a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting,
    9 remaining essentially unchanged
    10 </p>
    11 </>
    12 );
    13}

    Untuk membuat link antar halaman kita bisa gunakan component Link yang sudah disediakan oleh Next.js. Jadi kita tidak gunakan tag link .

    Component Link ini juga akan mengaktifkan client-side navigation, dimana proses peralihan dari satu halaman ke halaman lain dilakukan oleh JavaScript dan bukan browser.

    Update code halaman pages/index.js menjadi seperti ini:

    index.js

    1import Head from 'next/head';
    2import Link from 'next/link';
    3import styles from '../styles/Home.module.css';
    4
    5export default function Home() {
    6 return (
    7 <div className={styles.container}>
    8 <Head>
    9 <title>Create Next App</title>
    10 <link rel="icon" href="/favicon.ico" />
    11 </Head>
    12 <main className={styles.main}>
    13 <h1 className={styles.title}>Welcome to My Blog</h1>
    14 <div className={styles.grid}>
    15 <div className={styles.card}>
    16 <Link href="/posts/first-post">
    17 <h3>First Post</h3>
    18 </Link>
    19 <p>Lorem Ipsum is simply dummy text of the printing and typesetting industry...</p>
    20 </div>
    21 </div>
    22 </main>
    23 <footer className={styles.footer}></footer>
    24 </div>
    25 );
    26}

    Hasilnya:

    Layout Components

    Kita bisa membuat sebuah shared component yang bisa digunakan pada semua halaman lain dengan menggunakan Layout.

    Caranya adalah dengan membuat sebuah folder dengan nama components kemudian buat sebuah file baru layout.js dengan code berikut ini:

    1export default function Layout({ children }) {
    2 return <div>{children}</div>;
    3}

    Kemudian import component ke dalam file first-post.js.

    1import Layout from '../../components/layout';
    2
    3export default function FirstPost() {
    4 return (
    5 <Layout>
    6 <h1>First Post</h1>
    7 // more
    8 </Layout>
    9 );
    10}

    Untuk mengetahui kegunaan dari Layout component ini kita perlu buat satu halaman baru lagi.

    Buat sebuah file dengan nama second-post.js di dalam folder posts dengan code sebagai berikut:

    posts/second-post.js

    1import Layout from '../../components/layout';
    2
    3export default function SecondPost() {
    4 return (
    5 <Layout>
    6 <h1>Second Post</h1>
    7 <p>
    8 Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin
    9 literature from 45 BC, making it over 2000 years old
    10 </p>
    11 </Layout>
    12 );
    13}

    Dengan layout component, setiap code yang ditambahkan ke file layout.js akan muncul di kedua halaman post.

    Styles

    Salah satu penggunaan layout component adalah menambahkan style CSS ke banyak halaman tanpa harus menambahkannya satu per-satu.

    Kita akan tambahkan CSS ke semua halaman post…

    Buat sebuah file dengan nama layout.module.css di dalam folder components.

    Tambahkan code berikut ini:

    layout.module.css

    1.container {
    2 max-width: 36rem;
    3 padding: 0 1rem;
    4 margin: 3rem auto 6rem;
    5}

    Kemudian update layout.js:

    layout.js

    1import styles from './layout.module.css';
    2
    3export default function Layout({ children }) {
    4 return <div className={styles.container}>{children}</div>;
    5}

    Hasilnya:

    Global Styles

    Sedangkan jika ingin menerapkan sebuah style ke semua halaman, gunakan Global Styles.

    Untuk melakukannya kita update file styles/global.css.

    Ubah ukuran default dari font dan line-height.

    global.css

    1html,
    2body {
    3 padding: 0;
    4 margin: 0;
    5 font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica
    6 Neue, sans-serif;
    7 line-height: 1.6;
    8 font-size: 18px;
    9}
    10a {
    11 color: inherit;
    12 text-decoration: none;
    13}
    14* {
    15 box-sizing: border-box;
    16}

    Semua ukuran font pada blog sekarang berukuran 18px dengan jarak antar baris adalah 1.6.

    Asset

    Untuk menghandle asset seperti file gambar kita bisa simpan di dalam folder public.

    nextjs public folder

    Kita bisa akses file gambar cukup dengan menggunakan nama file tanpa path (relative path).

    1<img src="/my-image.png" alt="My Image" className="logo" />

    Hasil Akhir:

    nextjs add image

    Pre-rendering

    Next.js dapat melakukan dua bentuk pre-rendering yaitu SSR & SSG.

    Teknik pre-rendering membuat load halaman menjadi lebih cepat dibanding dengan teknik tanpa pre-rendering.

    Perbandingan

    Rendering vs Pre-rendering

    prerendering

    Sumber

    no prerendering

    Sumber

    SSG vs SSR

    SSG

    Halaman HTML digenerate pada saat proses build sebelum dikirim ke client/client mengirimkan request.

    nextjs ssg

    Sumber

    SSR

    Halaman HTML di generate untuk setiap request dari client.

    nextjs ssr

    Sumber

    Data Fetching

    Next.js menggunakan function yang berbeda untuk proses data fetching pada setiap teknik pre-rendering:

    • Untuk SSG kita gunakan function getStaticProps.

    • Untuk SSR kita gunakan function getServerSideProps.

    Pada bagian ini kita akan buat demo fetch data dari sebuah API menggunakan pre-rendering SSG.

    Membuat API

    Kita bisa gunakan package json-server untuk membuat API.

    Install json-server

    1$ yarn global add json-server

    Atau

    1$ npm install -g json-server

    Buat sebuah file di dalam komputer/laptop dengan nama db.json.

    db json

    Kemudian jalankan perintah berikut ini untuk start server.

    1$ json-server --watch db.json --port 3001

    Pastikan jalankan perintah ini dari dalam folder dimana db.json berada. Dan kita gunakan port 3001 karena port 3000 sudah digunakan oleh Next.js.

    Implementasi getStaticProps

    Tambah code berikut ini di bagian bawah component Home atau file index.js.

    index.js

    1...
    2
    3export async function getStaticProps() {
    4 const apiURL = "http://localhost:3001/posts";
    5 const response = await fetch(apiURL);
    6 const data = await response.json();
    7 return {
    8 props: {
    9 externalPostData: data,
    10 },
    11 };
    12}

    Props externalPostData dapat diakses di dalam component Home.

    index.js

    1...
    2export default function Home({ externalPostData }) {
    3...

    Kemudian data kita render sebagai daftar post:

    index.js

    1...
    2 {externalPostData.map((data) => {
    3 return (
    4 <div className={styles.card} key={data.id}>
    5 <Link href={data.link}>
    6 <h3>{data.title}</h3>
    7 </Link>
    8 <p>{data.excerpt}</p>
    9 </div>
    10 );
    11 })}
    12...

    Final Code dari index.js:

    1import Head from "next/head";
    2import Link from "next/link";
    3import styles from "../styles/Home.module.css";
    4
    5export default function Home({ externalPostData }) {
    6 return (
    7 <div className={styles.container}>
    8 <Head>
    9 <title>Create Next App</title>
    10 <link rel="icon" href="/favicon.ico" />
    11 </Head>
    12
    13 <main className={styles.main}>
    14 <img src="/my-image.png" alt="My Image" className="logo" />
    15 <h1 className={styles.title}>Welcome to My Blog</h1>
    16
    17 <div className={styles.grid}>
    18 <div className={styles.card}>
    19 <Link href="/posts/first-post">
    20 <h3>First Post</h3>
    21 </Link>
    22 <p>
    23 Lorem Ipsum is simply dummy text of the printing and typesetting
    24 industry...
    25 </p>
    26 </div>
    27 <div className={styles.card}>
    28 <Link href="/posts/second-post">
    29 <h3>Second Post</h3>
    30 </Link>
    31 <p>
    32 Contrary to popular belief, Lorem Ipsum is not simply random
    33 text...
    34 </p>
    35 </div>
    36 {externalPostData.map((data) => {
    37 return (
    38 <div className={styles.card} key={data.id}>
    39 <Link href={data.link}>
    40 <h3>{data.title}</h3>
    41 </Link>
    42 <p>{data.excerpt}</p>
    43 </div>
    44 );
    45 })}
    46 </div>
    47 </main>
    48
    49 <footer className={styles.footer}></footer>
    50 </div>
    51 );
    52}
    53
    54export async function getStaticProps() {
    55 const apiURL = "http://localhost:3001/posts";
    56
    57 const response = await fetch(apiURL);
    58
    59 const data = await response.json();
    60
    61 return {
    62 props: {
    63 externalPostData: data,
    64 },
    65 };
    66}

    Hasilnya:

    nextjs fetch api

    Data First Post dan Second Post berasal dari halaman internal atau file yang ada di dalam folder pages.

    Sedangkan data Third Post dan Last Post berasal dari API/json-server.

    Jika kita klik Third Post, Next.js akan mengarahkan ke alamat /posts/third-post, tetapi kita akan mendapatkan error seperti ini:

    nextjs not found

    Hal ini dikarenakan halaman untuk menampilkan data tersebut belum dibuat.

    Agar halaman dapat dibuat secara dinamis berdasarkan data yang didapat dari API kita bisa gunakan Dynamic Routes/URL.

    Dynamic Routes/ URLs

    Dengan dynamic routes kita bisa buat halaman berdasarkan external data seperti dari API.

    Kita akan gunakan function bernama getStaticPaths untuk generate path yang dibutuhkan.

    Buat sebuah file dengan nama [id].js di dalam folder pages/posts.

    folder structure id js

    Kemudian tambahkan code berikut ini:

    [id].js

    1import Layout from '../../components/layout';
    2
    3export default function Post() {
    4 return (
    5 <Layout>
    6 <h1></h1>
    7 <p></p>
    8 </Layout>
    9 );
    10}

    Di file yang sama buat function untuk mendapatkan data semua id dari setiap post:

    [id].js

    1...
    2async function getAllPostIds() {
    3 const apiUrl = "http://localhost:3001/posts";
    4
    5 const response = await fetch(apiUrl);
    6
    7 const allPosts = await response.json();
    8
    9 const allPostIds = allPosts.map((post) => {
    10 return { params: { id: post.alias } };
    11 });
    12
    13 return allPostIds;
    14}

    Yang perlu diperhatikan disini adalah bentuk data yang dihasilkan oleh function harus memiliki format seperti ini:

    1[
    2 {
    3 "params":{
    4 "id":"third-post"
    5 }
    6 },
    7 {
    8 "params":{
    9 "id":"last-post"
    10 }
    11 }
    12]

    Harus memiliki params dan id untuk setiap objectnya.

    Tanpa menggunakan format ini getStaticPaths akan gagal dieksekusi.

    Tambahkan function getStaticPaths:

    [id].js

    1...
    2export async function getStaticPaths() {
    3 const paths = await getAllPostIds();
    4 return {
    5 paths,
    6 fallback: false,
    7 };
    8}

    Kemudian tambahkan function untuk mendapatkan data tunggal dari post dengan parameter alias:

    [id].js

    1async function getPostData(id) {
    2 const apiURL = `http://localhost:3001/posts?alias=${id}`;
    3
    4 const response = await fetch(apiURL);
    5
    6 const postData = await response.json();
    7
    8 return postData;
    9}

    Function di atas akan digunakan di dalam function getStaticProps:

    [id].js

    1...
    2export async function getStaticProps({ params }) {
    3 const postData = await getPostData(params.id);
    4 return {
    5 props: {
    6 postData: postData[0],
    7 },
    8 };
    9}

    Jika kita menggunakan json-server, data dikirimkan dalam bentuk array, oleh karena itu untuk membaca data kita harus akses index array ke-0.

    postData: postData[0]

    Terakhir update component Post untuk menampilkan data dari getStaticProps:

    [id].js

    1export default function Post({ postData }) {
    2 return (
    3 <Layout>
    4 <h1>{ postData.title }</h1>
    5 <p>{ postData.body }</p>
    6 </Layout>
    7 );
    8}
    9
    10...

    Final Code:

    [id].js

    1import Layout from "../../components/layout";
    2
    3export default function Post({ postData }) {
    4 return (
    5 <Layout>
    6 <h1>{ postData.title }</h1>
    7 <p>{ postData.body }</p>
    8 </Layout>
    9 );
    10}
    11
    12async function getAllPostIds() {
    13 const apiUrl = "http://localhost:3001/posts";
    14
    15 const response = await fetch(apiUrl);
    16
    17 const allPosts = await response.json();
    18
    19 const allPostIds = allPosts.map((post) => {
    20 return { params: { id: post.alias } };
    21 });
    22
    23 return allPostIds;
    24}
    25
    26export async function getStaticPaths() {
    27 const paths = await getAllPostIds();
    28 return {
    29 paths,
    30 fallback: false,
    31 };
    32}
    33
    34async function getPostData(id) {
    35 const apiURL = `http://localhost:3001/posts?alias=${id}`;
    36
    37 const response = await fetch(apiURL);
    38
    39 const postData = await response.json();
    40
    41 return postData;
    42}
    43
    44export async function getStaticProps({ params }) {
    45 const postData = await getPostData(params.id);
    46 return {
    47 props: {
    48 postData: postData[0],
    49 },
    50 };
    51}

    Hasilnya:

    Deployment

    Ada beberapa tempat hosting yang bisa kita gunakan untuk deploy aplikasi next.js secara gratis. Dan yang paling mudah adalah menggunakan platform Vercel.

    Team Vercel adalah creator Next.js.

    Proses Deploy:

    1. Buat akun di Vercel
    2. Push repository ke github
    3. Import repository menggunakan menu New Project

    Vercel akan mendeteksi semua konfigurasi secara otomatis untuk selanjutnya di deploy. Setelah proses deploy berhasil, kita akan mendapatkan url untuk mengakses aplikasi Next.js.

    Lainnya

    Masih ada beberapa fitur dasar Next.js yang tidak dicover di dalam tutorial ini, seperti:

    • API Routes
    • Image optimization
    • Environment Variable
    • TypeScript Support

    Untuk lebih lengkapnya kamu bisa akses halaman offical Next.js.