Akses kelas selamanya

Ambil Promo
flash sale
hamburger-menu

Tips All

Meningkatkan skills menjadi 1% lebih baik

Reset
Kelas Panduan Memulai Proyek React, Tailwind CSS, dan TypeScript hingga Menjadi Satu Halaman Web di BuildWithAngga

Panduan Memulai Proyek React, Tailwind CSS, dan TypeScript hingga Menjadi Satu Halaman Web

Daftar Isi PendahuluanDownload Node JSDownload LaragonViteInstall React & TypescriptMasuk dan InstallTailwind CSSCara Memasukkan Tailwind ke ReactInstall Tailwind CSSKonfigurasiCSSHapusUbahTambahHasil Struktur Folder Setelah di TambahPastikan Sudah Download Dependency IniDownload Template HTML ShaynakitMasukin Template ke ReactCopy Gambar & IconConvert ke JSXAkses BrowserPenutup Pendahuluan Kalau kamu baru ingin mulai belajar membangun web modern, kombinasi React, Tailwind CSS, dan TypeScript adalah paket lengkap yang layak dicoba. Di artikel ini, kita akan bahas langkah-langkah praktis dari awal hingga kamu berhasil membuat satu halaman web yang rapi, responsif, dan mudah dikembangkan. Tenang, semuanya akan dijelaskan dengan sederhana dan jelas. Dimuali dari mendownload Node JS dulu. Download Node JS Browser - Node JS Node.js itu ibarat dapur tempat JavaScript bisa masak. Kalau biasanya JavaScript hanya bisa dipakai untuk mengatur tampilan di halaman web, dengan Node.js kamu bisa pakai JavaScript untuk bikin hal-hal di belakang layar, seperti menyajikan data dari database, membuat server sendiri, menjalankan React JS atau menjalankan alat bantu seperti Tailwind CSS. Dibangun di atas mesin cepat milik Google (V8), Node.js memungkinkan developer membangun aplikasi yang ringan dan responsif hanya dengan satu bahasa untuk frontend dan backend. Pastiin kamu sudah menginstall dan kalau mau cek hasilnnya ketik perintah ini di terminal kamu: node -v npm -v Kalau memang sudah terinstall bakal muncul kayak gini yang artinya udah siap di pake nanti : Download Laragon Browser - Laragon Laragon itu ibarat kotak perkakas praktis untuk ngoding PHP dan web development di Windows. Daripada repot install Apache, MySQL, PHP, dan lain-lain satu per satu, Laragon menyatukannya dalam satu aplikasi ringan yang siap pakai. Dengan antarmuka yang simpel, kamu bisa langsung buat proyek Laravel, WordPress, atau lainnya hanya dengan beberapa klik. Selain itu, Laragon juga cepat, portabel, dan nggak bikin sistem berat. Cocok banget buat kamu yang ingin mulai belajar backend tanpa ribet konfigurasi sana-sini. Pastiin kamu juga udah install Laragon ini, santai boleh pakai XAMPP karena sama aja, yang kita butuhin itu MySQL nya agar bisa nyambung React kita ke Database kita. Kalau udah berhasil install Laragon dan udah siap ciri-cirinya kayak gini: Result & Play Laragon Biar makin yakin udah bener, ketik http://localhost di browser kamu dan hasilnya harus gini: Second Test Laragon Vite Browser - Vite Vite adalah build tool modern yang bikin proses ngoding frontend jadi lebih cepat dan menyenangkan, terutama buat proyek React, Vue, atau lainnya. Saat kamu mulai ngoding, Vite langsung nyala dan tampilkan hasilnya tanpa loading lama, karena dia cuma memproses file yang benar-benar dibutuhkan. Pas udah siap dibuild untuk production, dia juga ngerapihin semuanya jadi ringan dan siap online. Selain itu, Vite didesain dengan arsitektur yang memanfaatkan kemampuan browser modern, jadi proses pengembangan terasa lebih responsif dan minim hambatan. Dengan fitur hot module replacement (HMR) yang super cepat, perubahan kode langsung terlihat tanpa perlu reload halaman penuh, membuat workflow kamu jadi jauh lebih efisien dan menyenangkan. Bisa dibilang, Vite itu seperti mesin kopi instan, sekali tekan, langsung jadi, beda dengan alat lama yang harus dipanaskan dulu dan bikin kamu nunggu lama sebelum bisa mulai ngoding. Vite ini bakal kita pake buat install React nya biar nanti jalannya lebih powerful. Install React & Typescript Nah, sekarang setelah kita udah ngerti apa yang dibutuhin dan alat-alatnya juga udah siap, tinggal gas bikin proyeknya! Buat dulu folder terserah kamu mau kasih nama apa, terus buka folder itu di VSCode atau text editor favorit kamu sampai tampilannya kayak gambar ini: VSCode - First Folder Untuk langkah selanjutnya, buka terminal di dalam VSCode dengan cara tekan tombol CTRL + ` (tombol backtick, biasanya di bawah Esc). Setelah terminal muncul, kamu bisa langsung jalankan perintah di bawah ini untuk mulai setup proyek kamu: npm create vite@latest Pilih aja kayak yang di gambar di bawah ini, pilih React, TypeScript + SWC, dan beri nama proyek terserah kalian: VSCode - Create Project Masuk dan Install Nah setelah itu tinggal masuk ke folder proyeknya lalu jalanin kayak gini: cd belajar npm install npm run dev Jadi, cd itu singkatan dari “change directory” alias masuk ke dalam folder. Nah, kalau kamu ketik cd belajar, artinya kamu lagi pindah masuk ke folder yang namanya “belajar”. Terus, npm install itu perintah buat ngunduh dan memasang semua paket atau library yang dibutuhin proyek kamu supaya bisa jalan dengan lancar. Kalau npm run dev, itu perintah buat ngejalanin proyek kamu dalam mode development, jadi kamu bisa lihat hasilnya langsung dan kalo ada perubahan, tampilannya bakal otomatis update tanpa perlu di-refresh manual. Lalu akses link dibawah ini, nanti proyeknya bakal tampil: VSCode - Ready to Appear Kalau sudah kamu akses link itu tampilannya bakal gini: Browser - Result Project Tailwind CSS Browser - Tailwind CSS Jadi, Tailwind CSS itu kayak kotak peralatan serbaguna buat desain website. Bayangin kamu lagi ngegambar dan biasanya harus bikin kuas dan cat sendiri dulu, nah Tailwind ini sudah sediain ratusan kuas kecil yang tinggal kamu pakai langsung buat bikin gambar sesuai keinginan. Nggak perlu ribet nulis kode CSS panjang-panjang, kamu cuma pakai kelas-kelas kecil kayak bg-blue-500 buat warna biru, p-4 buat padding, atau flex buat atur layout, langsung tempel di HTML kamu. Selain cepat dan praktis, Tailwind juga bikin tampilan website kamu gampang diatur dan konsisten karena semuanya seragam dari alat yang sama. Nah, kalau mau warna atau ukuran yang beda, tinggal atur di file konfigurasi, kayak nge-mix cat supaya warna yang kamu pakai pas banget. Intinya, pakai Tailwind itu kayak punya toolkit yang siap pakai buat bikin tampilan keren tanpa harus repot bikin semuanya dari nol. Cara Memasukkan Tailwind ke React Install Tailwind CSS Sekarang, tinggal jalanin perintah ini di terminal yang ada di dalam folder proyek React kamu. Cukup ketik aja perintahnya, terus pencet Enter, nanti prosesnya bakal jalan otomatis: npm install tailwindcss @tailwindcss/vite Perintah npm install tailwindcss @tailwindcss/vite ini fungsinya buat ngunduh dan pasang paket Tailwind CSS beserta plugin khusus yang bikin Tailwind bisa jalan lancar bareng Vite sebagai build tool-nya. Jadi, dengan perintah ini, kamu nggak cuma dapetin core Tailwind CSS buat styling, tapi juga integrasi yang mulus supaya Vite bisa proses Tailwind dengan cepat dan efisien saat kamu lagi develop proyek frontend-mu. Singkatnya, ini langkah penting supaya styling pakai Tailwind bisa berjalan sempurna di proyek React yang kamu bikin pakai Vite. Hasilnya VSCode Terminal - Install Tailwind CSS Konfigurasi Di dalam file vite.config.ts kamu beri kode ini import tailwindcss from '@tailwindcss/vite' Kalimat import tailwindcss from '@tailwindcss/vite' itu artinya kita lagi ngambil (import) plugin Tailwind CSS khusus yang dibuat untuk Vite ke dalam file konfigurasi atau skrip kita. Dengan nge-import ini, kita kasih tahu Vite supaya bisa pakai Tailwind CSS secara optimal dan nyatu banget sama proses build dan development yang lagi jalan. Jadi, plugin ini semacam jembatan yang bikin Tailwind dan Vite kerja barengan dengan mulus tanpa ribet. tailwindcss() tailwindcss() itu kayak kamu lagi memanggil atau ngejalanin fungsi Tailwind CSS di dalam konfigurasi Vite. Jadi, pas kamu tulis tailwindcss(), artinya kamu ngasih perintah supaya Vite aktifkan plugin Tailwind CSS yang tadi sudah di-import, supaya bisa langsung digunakan buat proses styling proyek kamu. Bisa dibilang, ini kayak tombol “ON” buat Tailwind di proyek kamu. Sampai hasilnya gini import { defineConfig } from "vite"; import react from "@vitejs/plugin-react-swc"; import tailwindcss from "@tailwindcss/vite"; // <https://vite.dev/config/> export default defineConfig({ plugins: [react(), tailwindcss()], }); CSS Di file src/index.css, hapus semua kode yang ada supaya kosong, lalu tinggal isi cuma dengan satu baris ini saja: @import "tailwindcss"; Sekarang Tailwind sudah nyantol ke proyek React kita, jadi tinggal ambil css nya terus pake. Hapus VSCode - Structure Project Hapus file App.css karena file tersebut enggak akan kita pake. Ubah Lalu hapus isi file ini : VSCode - App.tsx Isi dengan: VSCode - New Code Kode <BrowserRouter>, <Routes>, dan <Route> yang kamu taruh di file App.tsx di React itu fungsinya buat ngatur routing atau navigasi antar halaman di aplikasi kamu. BrowserRouter itu kayak pembungkus utama yang ngatur semua rute berdasarkan URL di browser, jadi React tahu harus nampilin halaman apa pas user akses URL tertentu.Routes adalah tempat di mana kamu naro semua daftar rute (alias halaman) yang mau kamu buat.Nah, Route itu satuan rute-nya. Di contoh kamu, pas user buka / (halaman utama), maka React bakal nampilin komponen <HomePage />. Jadi, kode itu intinya nge-set: "Kalau URL-nya adalah /, tampilkan halaman HomePage." Simple dan terstruktur banget buat aplikasi yang butuh lebih dari satu halaman. Itu error karna kita belum buat filenya dan belum download depedencynya, tapi santai aja nanti bakal kita lakuin. Tambah Buat sekarang tambahin dulu file dan folder-folder keperluannya kayak: .env File .env di React itu fungsinya buat nyimpen data rahasia atau pengaturan penting yang nggak mau kamu tulis langsung di dalam kode. Misalnya kayak API key, URL backend, token, atau config lain yang bisa beda-beda antara development dan production. Dengan begitu, kamu bisa akses data itu lewat process.env.NAMA_VARIABEL di dalam kode kamu, dan nilai aslinya diambil dari file .env. Nah, di React (pakai Vite atau CRA), nama variabelnya harus diawali dengan VITE_ (kalau pakai Vite) atau REACT_APP_ (kalau pakai CRA), misalnya VITE_API_URL=https://api.example.com. Jadi, pakai .env itu bikin proyek kamu lebih aman, rapi, dan gampang diatur. Buat file ini di path terluar terus isi gini: VSCode - env VITE_REACT_API_URL = <http://127.0.0.1:8000/api> VITE_REACT_API_STORAGE_URL = <http://127.0.0.1:8000/storage> Nah, keduanya itu berfungsi buat nyimpen alamat URL yang bakal dipakai di dalam aplikasi React kita. VITE_REACT_API_URL biasanya dipakai buat ngarahin semua request API ke backend, misalnya ke Laravel atau server lokal kita yang lagi jalan di http://127.0.0.1:8000/api, sedangkan VITE_REACT_API_STORAGE_URL dipakai buat akses file atau gambar yang disimpan di direktori storage backend. Dengan naruh ini di .env, kita bisa panggil variabelnya lewat import.meta.env.VITE_REACT_API_URL atau import.meta.env.VITE_REACT_API_STORAGE_URL di file React, jadi nanti kalau mau ganti alamat server tinggal ubah di .env aja tanpa perlu utak-atik banyak file. api Folder api yang kita taruh di dalam folder src di proyek React (yang pakai Vite) biasanya dipakai buat nyimpen semua fungsi atau file yang berhubungan sama komunikasi ke backend atau API. Misalnya, di folder api ini kita bisa bikin file kayak auth.ts, user.ts, atau produk.ts yang isinya fungsi-fungsi buat ngirim request ke server, misalnya login, ambil data user, atau ambil daftar produk. Tujuannya biar kode kita lebih rapi dan terorganisir, jadi logika komunikasi ke server nggak nyampur sama komponen tampilan (UI). Jadi, tiap komponen cukup "minta data" dari folder api, dan nggak perlu tahu detail cara ambil datanya gimana. Kayak punya bagian khusus buat ngobrol sama server, biar semua rapi dan enak dikelola. components Folder components di dalam folder src di proyek React yang pakai Vite fungsinya buat nyimpen semua komponen UI yang kita bangun dan bisa dipakai ulang di berbagai bagian aplikasi. Komponen ini bisa berupa tombol, header, card, form, atau elemen-elemen kecil lainnya yang tampil di layar. Dengan memisahkannya ke dalam folder components, struktur proyek jadi lebih rapi dan modular, jadi kalau kita mau ubah tampilan tombol misalnya, tinggal edit satu file tanpa harus cari-cari di banyak tempat. Intinya, folder ini jadi tempat ngatur bagian-bagian kecil dari tampilan yang bisa disusun bareng untuk membentuk halaman yang utuh. context Folder context di dalam folder src di proyek React (yang pakai Vite) fungsinya buat nyimpen file-file yang berkaitan dengan manajemen state global pakai React Context API. Jadi kalau kita punya data atau state yang perlu diakses dari banyak komponen, misalnya data user yang sudah login, tema aplikasi (dark/light), atau status autentikasi, kita bisa simpan dan kelola datanya lewat Context. Dengan adanya folder context, kita bisa bikin file seperti AuthContext.tsx atau ThemeContext.tsx yang masing-masing isinya menyediakan Provider dan useContext supaya komponen lain bisa ikut "nebeng" data dari satu sumber. Jadi, folder ini membantu kita bikin manajemen state lebih terstruktur, konsisten, dan gampang di-maintain. hooks Folder hooks di dalam folder src di proyek React yang pakai Vite gunanya buat nyimpen custom hooks, yaitu fungsi-fungsi buatan sendiri yang memanfaatkan fitur React Hooks (kayak useState, useEffect, dll) untuk menangani logika tertentu yang bisa dipakai ulang di berbagai komponen. Misalnya, kita bisa bikin useAuth.ts buat ngecek status login, useFetch.ts buat ambil data dari API, atau useWindowSize.ts buat deteksi ukuran layar. Dengan naruh semua custom hooks di folder hooks, struktur proyek jadi lebih rapi, dan logika yang sering dipakai bisa ditulis sekali lalu dipanggil di mana pun dibutuhkan. Intinya, folder ini bantu kita pisahin logika dari tampilan supaya lebih modular dan mudah dirawat. pages Folder pages di dalam folder src di proyek React yang pakai Vite fungsinya buat nyimpen file-file komponen yang mewakili halaman utama dalam aplikasi, seperti halaman beranda (HomePage.tsx), login (LoginPage.tsx), atau profil (ProfilePage.tsx). Berbeda dari folder components yang isinya bagian-bagian kecil UI, folder pages biasanya berisi komponen tingkat tinggi yang menyusun berbagai komponen jadi satu tampilan halaman utuh. Folder ini juga sering dikaitkan langsung dengan routing (pakai React Router), jadi setiap file di pages biasanya cocok dengan satu URL di aplikasi. Dengan begitu, struktur aplikasi jadi lebih teratur dan gampang diatur kalau proyek makin besar. providers Folder providers di dalam folder src di proyek React yang pakai Vite biasanya dipakai buat nyimpen komponen-komponen provider yang membungkus aplikasi atau bagian tertentu dengan konteks khusus. Provider ini biasanya berisi komponen yang menggunakan React Context API, Redux Provider, Theme Provider, atau library lain yang butuh “membungkus” komponen supaya data atau fungsi tertentu bisa diakses turun-temurun oleh komponen-komponen di bawahnya. Misalnya, AuthProvider buat ngatur status login, atau ThemeProvider buat ngatur tema warna aplikasi. Dengan punya folder providers, kita bisa kelola dan pisahkan logika penyedia data global secara rapi dan terstruktur, jadi saat mau pakai atau update provider, gampang ditemukan dan di-maintain. routes Folder routes di dalam folder src di proyek React yang pakai Vite fungsinya buat ngatur semua konfigurasi routing atau jalur navigasi aplikasi. Di sini kita biasanya bikin file yang mendefinisikan bagaimana URL di aplikasi kita dipetakan ke komponen halaman tertentu, misalnya menentukan kalau path / tampil HomePage, /login tampil LoginPage, dan seterusnya. Dengan naruh semua aturan routing di folder routes, kode jadi lebih terorganisir dan terpisah dari logika UI. Jadi kalau mau nambah, ubah, atau cek navigasi aplikasi, kita tinggal buka folder ini tanpa harus nyari di berbagai file. Ini penting banget supaya aplikasi React kita gampang dikelola, apalagi kalau sudah besar dan punya banyak halaman. schemas Folder schemas di dalam folder src di proyek React yang pakai Vite biasanya dipakai buat nyimpen definisi struktur data atau aturan validasi yang digunakan di aplikasi. Misalnya, kita bisa bikin schema untuk validasi form dengan library seperti Yup atau Zod, di mana schema ini menjelaskan aturan seperti “email harus valid”, “password minimal 8 karakter”, atau “nama tidak boleh kosong”. Selain itu, folder schemas juga bisa berisi tipe data atau interface TypeScript yang mendeskripsikan bentuk data yang kita pakai, supaya kode lebih terstruktur dan aman dari kesalahan tipe data. Dengan punya folder schemas, kita bisa mengatur aturan dan tipe data di satu tempat khusus, bikin aplikasi lebih rapi dan gampang dipelihara. services Folder services di dalam folder src di proyek React yang pakai Vite fungsinya buat menyimpan logika bisnis dan fungsi-fungsi yang berhubungan dengan interaksi ke luar aplikasi, seperti komunikasi dengan API, pengolahan data, atau fitur khusus yang butuh dipisah dari komponen UI. Misalnya, di sini kita bisa bikin file untuk fungsi-fungsi yang handle request ke backend, seperti userService.ts yang isinya fungsi untuk login, register, atau ambil data user, atau paymentService.ts yang urus proses pembayaran atau piService.ts untuk mengakses axios. Dengan menaruh semua logika ini di folder services, kode jadi lebih terorganisir dan komponen UI bisa fokus hanya pada tampilan dan interaksi pengguna tanpa kebingungan dengan logika bisnis. Jadi, folder services bantu kita pisahkan tanggung jawab kode supaya aplikasi lebih modular dan gampang di-maintain. Beri file ini dan isi ini: VSCode - Axios types Folder types di dalam folder src di proyek React yang pakai Vite itu tempat kita nyimpen semua definisi tipe data atau interface TypeScript yang dipakai di seluruh aplikasi. Jadi kalau kita mau jelasin bentuk data, properti apa saja yang ada, atau tipe dari variabel tertentu, kita taruh di sini supaya kode lebih terstruktur dan gampang dipakai ulang. Dengan begitu, kita bisa lebih aman dan terhindar dari error tipe data karena semua tipe sudah didefinisikan dengan jelas dan bisa dipanggil di mana saja dalam proyek. Intinya, folder types bikin pengelolaan tipe data jadi rapi dan efisien. Contohnya seperti ini: VSCode - TypeScript utils Folder utils di dalam folder src di proyek React yang pakai Vite itu tempat kita nyimpen berbagai fungsi kecil atau helper yang bisa dipakai ulang di banyak bagian aplikasi. Misalnya, fungsi buat format tanggal, hitung sesuatu, atau ngecek validasi sederhana yang nggak spesifik ke satu fitur tertentu. Dengan naruh fungsi-fungsi ini di folder utils, kita jadi nggak perlu nulis ulang kode yang sama berkali-kali, dan struktur proyek jadi lebih bersih karena fungsi-fungsi bantuan ini terpisah dari logika utama aplikasi. Jadi, utils itu kayak kotak alat kecil yang selalu siap dipakai kapan pun dibutuhkan. Hasil Struktur Folder Setelah di Tambah VSCode - Complete Structure Pastikan Sudah Download Depedency Ini Setelah memasukkan kode ini ke package.json langsung saja jalankan perintah npm install "dependencies": { "@hookform/resolvers": "^5.0.1", "@tailwindcss/vite": "^4.1.4", "@tanstack/react-query": "^5.74.4", "axios": "^1.9.0", "date-fns": "^4.1.0", "react": "^19.1.0", "react-dom": "^19.1.0", "react-router-dom": "^7.5.2", "tailwindcss": "^4.1.4", "zod": "^3.24.3" }, @hookform/resolvers: Plugin yang menghubungkan library validasi eksternal (seperti Zod atau Yup) ke React Hook Form, supaya kita bisa validasi form dengan aturan yang lebih kompleks.@tailwindcss/vite: Plugin resmi Tailwind CSS buat integrasi dengan Vite. Ini bikin proses build dan hot reload Tailwind jadi lebih cepat dan lancar.@tanstack/react-query: Library buat manajemen data fetching dan caching di React. Ngebantu kita ambil data dari API tanpa ribet urus loading, error, atau re-fetch otomatis.axios: Library populer buat melakukan request HTTP (GET, POST, dll). Dipakai buat komunikasi antara frontend dan backend secara lebih simpel dibanding fetch.date-fns: Kumpulan fungsi JavaScript buat memanipulasi tanggal. Mirip kayak Moment.js, tapi lebih ringan dan modular.react: Library utama untuk bikin UI berbasis komponen. Semua proyek React pasti butuh ini.react-dom: Library yang ngurusin gimana komponen React dirender ke dalam DOM browser. Jadi pasangan utamanya react.react-router-dom: Library untuk navigasi dan routing di aplikasi React berbasis web. Ngebantu kita bikin banyak halaman dalam satu aplikasi SPA (Single Page Application).tailwindcss: Framework CSS utility-first yang bikin styling jadi cepat dan konsisten langsung dari HTML atau JSX tanpa harus nulis CSS panjang-panjang.zod: Library validasi dan parsing data yang mirip Yup, tapi lebih ketat dan cocok banget dipakai bareng TypeScript karena integrasi tipenya yang solid. Download Template HTML Shaynakit Shaynakit - Ngekos Template HTML Di tutorial kali ini, kita bakal pakai proyek dari Shaynakit buat ngubahnya ke React. Kabar baiknya, proyek ini gratis dan kodenya juga udah disediain. Jadi kamu bisa langsung pakai, atau modif-modif dikit biar sesuai sama kebutuhan. Nggak perlu mulai dari nol proses ngodingnya jadi jauh lebih gampang dan cepet. Shaynakit sendiri tuh semacam website yang nyediain kumpulan desain bareng sama kodenya. Ada yang gratis, ada juga yang premium. Tapi tenang aja, yang gratis juga nggak pelit-pelit banget kok biasanya cuma dibatesin di jumlah halamannya aja. Nah, kalau yang premium, tentu lebih lengkap dan komplit, semua bagian udah siap pakai. Cara donwloadnya kayak gini: Buka situs utama ShaynaKit di https://shaynakit.com/landing.Klik menu atau tombol Register atau langsung buka https://shaynakit.com/register untuk membuat akun terlebih dahulu.Setelah berhasil mendaftar dan login, buka halaman template kode yang akan digunakan di https://shaynakit.com/details/ngekos-find-house-details-bokking-success-html-tailwind-css-template.Klik tombol Download, lalu pada opsi yang tersedia, pilih Free Trial.Klik tombol Start Today untuk memulai akses gratis.Setelah itu, kembali ke halaman yang sama: https://shaynakit.com/details/ngekos-find-house-details-bokking-success-html-tailwind-css-template, dan klik tombol Download sekali lagi.File template code akan terunduh dalam format .zip.Simpan file .zip tersebut ke dalam folder lokal proyek Anda, misalnya ./source-code/ngekos.zip. Masukin Template ke React Kalau udah download selanjutnya kita masukin 1 file ke proyek React. Copy Gambar & Icon buat pakai gambar dan icon dari template HTML yang udah kamu download tadi, kamu cukup cari folder yang namanya assets , biasanya isinya gambar, icon, dan file pendukung lainnya. Nah, tinggal klik folder assets itu, tekan Ctrl + C buat copy, lalu buka proyek React kamu, masuk ke folder public, dan paste di situ (Ctrl + V). Hasil akhirnya nanti folder assets itu bakal nongkrong di dalam public, jadi kamu bisa akses gambarnya langsung dari path kayak /assets/nama-file.png . Letaknya kayak gini: VSCode - Moving the assets Folder Convert ke JSX Setelah kamu download file .zip-nya, jangan lupa klik kanan → Extract All, biar semua isi file-nya bisa kita akses. Nah, abis itu buka folder hasil ekstrak-nya pakai VSCode. Begitu masuk, cari file yang namanya index.html, buka, dan salin dari tag <main> yang pembuka sampai tag </main> yang penutup, pokoknya seluruh isi <main> itu. Setelah itu, tinggal paste ke website yang ada di bawah ini: Convert HTML To JSX Lalu tombol “Copy” berwarna biru di pojok atas kanan itu klik untuk mengcopy tag jsxnya, lalu kembali ke VSCode, paste ke dalam HomePage.tsx kayak gini: Result Paste Akses Browser Lalu jalankan perintah npm run dev Hasil Browser - Result Nah, kalau tampilannya sekarang kelihatan berantakan, tenang dulu, itu bukan berarti ada yang salah. Sebenarnya tampilannya cuma belum sempurna karena belum ada Swiper JS-nya yang dipakai buat fitur scroll-slide di template aslinya. Jadi wajar aja kalau elemen yang seharusnya bisa digeser (kayak carousel atau slider) sekarang malah numpuk ke bawah atau nggak bisa digeser sama sekali. Solusinya simpel: tinggal install Swiper JS untuk React dan terapin di komponen yang butuh scroll. Begitu Swiper-nya udah kepasang dan disetting, tampilannya bakal mirip persis sama template HTML awalnya. Jadi tenang, tinggal satu langkah lagi nih buat bikin semuanya kelihatan rapi dan interaktif. Dan agar kamu mahir pake Tailwind sekaranglah waktunya challenge coba install dan terapi Swiper JS nya biar React kamu makin gacor. Penutup Setelah kamu ikutin semua langkahnya, harapannya kamu nggak cuma berhasil bikin satu halaman web yang keren, tapi juga makin paham cara kerja proyek modern berbasis React, Tailwind CSS, dan TypeScript. Jangan takut buat eksplor lebih jauh, karena dari sini kamu udah punya pondasi kuat buat bangun proyek yang lebih besar dan kompleks ke depannya. Semangat ngoding, dan selamat mencoba! 🚀

Kelas Perbedaan useState dan useEffect: Panduan React Hook Dasar di BuildWithAngga

Perbedaan useState dan useEffect: Panduan React Hook Dasar

Kalau kamu baru terjun ke dunia React atau lagi transisi dari class component ke function component, pasti sering banget ketemu sama dua hook populer ini: useState dan useEffect. Hooks adalah fitur di React yang memungkinkan kamu “menyisipkan” fitur-fitur React seperti state dan lifecycle ke dalam functional component. Sebelum ada hooks, fitur-fitur ini cuma bisa dipakai di class component. Kenapa penting? Functional component lebih simpel, ringan, dan mudah ditulis.Dengan hooks, kamu bisa mengatur state dan efek samping tanpa harus repot-repot bikin class. Artikel ini cocok banget buat kamu yang masih bingung: "Ini kapan sih pakai useState, dan kapan pakai useEffect?" Persiapan Proyek Sebelum kita mulai ngulik komponen di React, tentu kita perlu siapkan dulu proyek React nya, biar bisa langsung praktek dan nggak cuma teori doang. Syarat Awal Pastikan kamu udah install Node.js di komputer kamu, minimal versi 14 ya. Buat ngecek, buka terminal atau command prompt terus ketik: node -v npm -v Ini versi yg saya gunakan: Terminal Windows Kalau keluar versinya berarti udah oke, kalo belum, kamu bisa download Node.js dari situs resmi nodejs.org. Instalasi React dengan Next.js Nah, buat mulai proyek React, kita bakal pake Next.js. Next.js ini keren banget karena selain React biasa, dia juga punya fitur kayak routing otomatis dan server-side rendering yang bikin performa aplikasi makin ngebut. Buka terminal terus ketik perintah ini buat bikin proyek baru: npx create-next-app@latest bwa-react Jika ada konfirmasi seperti ini tekan Enter di keyboard Terminal: Install Next.js √ Would you like to use TypeScript? ... Pilih Yes√ Would you like to use ESLint? ... Pilih Yes√ Would you like to use Tailwind CSS? ... Pilh Yes√ Would you like your code inside a src/ directory? ... Pilih Yes√ Would you like to use App Router? (recommended) ... Pilih Yes√ Would you like to use Turbopack for next dev? ... Pilih Yes√ Would you like to customize the import alias (@/* by default)? ... Pilih No Terminal: Install Next.js Jika proses intallasi sudah selesai, masuk ke folder proyeknya: cd bwa-react Terus jalankan development server dengan perintah: npm run dev Terminal: Menjalankan Next.js Biasanya bakal keluar alamat http://localhost:3000 di terminal. Kamu buka alamat itu di browser, dan… voila! Kamu udah punya proyek React dengan Next.js siap pakai. Tampilan awal Next.js Kalau kamu pake bun, tinggal ganti aja npm jadi bun di perintah-perintah tadi. Apa Itu useState? useState adalah hook buat nyimpen dan mengatur data (state) di dalam komponen. Ibaratnya kayak laci pribadi komponen buat nyimpan data yang bisa berubah. Bentuk Umum: const [count, setCount] = useState<number>(0); count: nilai sekarang.setCount: fungsi buat update nilai count. Contoh Penggunaan: Counter Sederhana: 'use client'; import { useState } from 'react'; export default function Counter() { const [count, setCount] = useState<number>(0); return ( <div> <p>Jumlah: {count}</p> <button onClick={() => setCount(count + 1)}>Tambah</button> </div> ); } Form Input 'use client'; import { useState } from 'react'; export default function InputForm() { const [name, setName] = useState<string>(''); return ( <input value={name} onChange={(e) => setName(e.target.value)} placeholder="Nama kamu" /> ); } Kapan pakai useState? Kalau kamu pengen nyimpan data yang: Bisa berubah (dinamis)Dibutuhkan saat render ulang Apa Itu useEffect? useEffect adalah hook buat ngejalanin efek samping (side effects). Maksudnya, hal-hal yang nggak langsung berhubungan sama proses render React, kayak: Ambil data dari APIManipulasi document.titleSimpan data ke localStorageTimer/intervalEvent listener Bentuk Umum: useEffect(() => { // efek samping di sini }, [dependency]); Jenis-jenis Dependensi: DependensiKapan JalanKosong []Cuma sekali, setelah komponen pertama kali muncul (mount)Tidak adaTiap kali renderAda nilai [x]Jalan kalau x berubah Contoh: Ubah Title Tab 'use client'; import { useEffect, useState } from 'react'; export default function TitleChanger() { const [count, setCount] = useState<number>(0); useEffect(() => { document.title = `Klik: ${count}`; }, [count]); return <button onClick={() => setCount(count + 1)}>Klik aku!</button>; } Kapan Pakai useState vs useEffect? SituasiPakai Hook Apa?PenjelasanNyimpen nilai input formuseStateKarena kita butuh update data saat user ketikFetch data dari APIuseEffectIni termasuk efek sampingHitung jumlah klik tomboluseStateKarena data klik disimpan di stateUpdate judul tab browseruseEffectKarena manipulasi DOM dilakukan di luar ReactSimpan data ke localStorage saat state berubahuseState + useEffectGunakan state buat nyimpen, efek untuk nyimpan ke localStorage Contoh Praktis: Simpan Nama ke localStorage Studi Kasus Kita mau: Ambil nama dari localStorage pas komponen pertama kali muncul.Update localStorage setiap kali nama berubah. Kode Lengkap: Buat file pada direktori src/components/InputName.tsx // src/components/InputName.tsx "use client"; import { useState, useEffect } from "react"; export default function UserForm() { const [name, setName] = useState<string>(""); // Ambil nama dari localStorage saat pertama render useEffect(() => { const savedName = localStorage.getItem("username"); if (savedName) { setName(savedName); } }, []); // Simpan ke localStorage setiap kali 'name' berubah useEffect(() => { localStorage.setItem("username", name); }, [name]); // Fungsi untuk menghapus nama dari localStorage dan state const handleDelete = () => { localStorage.removeItem("username"); setName(""); }; return ( <div className="flex flex-col w-3xs space-y-4"> <input type="text" value={name} onChange={(e) => setName(e.target.value)} placeholder="Masukkan nama kamu" className="border px-4 py-2 rounded w-full" /> <button onClick={handleDelete} disabled={!name} className="ml-auto bg-red-500 text-white px-4 py-2 rounded hover:bg-red-600 transition-all duration-300 disabled:opacity-50" > Hapus Nama </button> </div> ); } 📌 Penjelasan: Saat pertama kali render, kita cek localStorage.Setelah user ngetik dan name berubah, kita simpan ulang ke localStorage.handleDelete() akan:Menghapus data dari localStorage dengan removeItem('username').Reset state name ke string kosong.Efek: input akan langsung kosong, dan tidak disimpan lagi ke localStorage sampai pengguna mengetik ulang. Buka page.tsx lalu ubah kode jadi seperti berikut: // scr/app/page.tsx import InputName from "@/components/InputName"; export default function Home() { return ( <div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]"> <main className="flex flex-col gap-[32px] row-start-2 items-center sm:items-start"> <InputName /> </main> </div> ); } Hasilnya: Contoh kombinasi useEffect dan useState Kesalahan Umum yang Sering Dilakukan Belajar React Hooks itu seru, tapi banyak juga jebakan Batman-nya. Nah, biar kamu nggak jatuh ke lubang yang sama, ini dia beberapa kesalahan umum yang sering banget terjadi pas pakai useState dan useEffect: Lupa Menambahkan Dependency di useEffect Contoh salah: useEffect(() => { console.log(name); }, []); // name seharusnya masuk ke dependency Penjelasan: Kalau name nggak dimasukin ke dependency array, useEffect nggak bakal ngejalanin ulang efeknya meskipun name berubah. Ini bisa bikin bug yang susah dilacak. Solusi: useEffect(() => { console.log(name); }, [name]); Bikin Infinite Loop Tanpa Sadar Contoh salah: const [count, setCount] = useState(0); useEffect(() => { setCount(count + 1); // ini bakal terus-menerus nge-trigger re-render }, [count]); Penjelasan: Karena setCount di-trigger tiap kali count berubah, efek ini akan terus jalan, bikin infinite loop dan nge-crash aplikasi. Solusi: Hindari update state di dalam useEffect yang bergantung pada state itu sendiri, kecuali kamu tahu persis apa yang kamu lakukan (misalnya pakai kondisi). Naruh Logika Berat di useState const [result, setResult] = useState(() => { // ini dieksekusi setiap render return hitungLamaBanget(); }); Solusi: Kalau hitungannya berat, kamu bisa manfaatkan fungsi inisialisasi useState: const [result, setResult] = useState(() => hitungLamaBanget()); // cuma dieksekusi sekali Efek Samping Tanpa Cleanup Misalnya pakai timer atau event listener tapi nggak dibersihin. Contoh salah: useEffect(() => { window.addEventListener('resize', handleResize); }, []); Solusi: useEffect(() => { window.addEventListener('resize', handleResize); return () => { window.removeEventListener('resize', handleResize); }; }, []); Terlalu Banyak useEffect yang Redundant Kadang kita bisa gabungkan beberapa useEffect jadi satu daripada bikin 3 efek berbeda yang sebenarnya bisa dikelola bareng. Tips: Kelola efek yang berkaitan di satu useEffect, supaya lebih rapi dan terkontrol. Tips dan Best Practice ❌ Jangan pakai useEffect buat logika render biasa (misal if, map, dll).🔄 Hindari infinite loop: pastikan dependensinya benar.🧹 Pakai cleanup kalau ada efek yang harus dibersihkan (interval, event listener). Contoh Cleanup: useEffect(() => { const interval = setInterval(() => { console.log('Running...'); }, 1000); return () => { clearInterval(interval); }; }, []); Kesimpulan 🧠 useState: Buat nyimpen dan update data lokal di komponen.⚡ useEffect: Buat efek samping di luar React seperti API, localStorage, DOM.Kombinasikan keduanya buat bikin komponen interaktif dan dinamis. Kalau komponenmu butuh “ingat sesuatu” dan “ngelakuin sesuatu di luar React”, maka useState dan useEffect adalah senjata wajib kamu. 📚 Artikel Terkait Biar makin mantap belajar React, kamu bisa lanjut baca artikel-artikel berikut: Mengenal Komponen di React JS: Functional vs Class Pelajari perbedaan antara komponen class dan functional, serta kenapa sekarang banyak developer lebih memilih functional component + hooks!Cara Deploy Website Statis ke Vercel Lewat GitHub (Lengkap + Contoh) Setelah paham useState dan useEffect, saatnya publish proyek kamu ke internet. Artikel ini panduannya step-by-step! 🎓 Rekomendasi Kelas Gratis Mau langsung praktik dan bikin project beneran? Coba kelas gratis dari BuildWithAngga berikut: 🔗 Membuat Website Voucher Game dengan Terintegrasi API VocaGame Di kelas ini kamu bakal belajar cara ambil data dari API, pakai React Hooks (yes, termasuk useState dan useEffect!), dan bikin tampilan interaktif. Cocok banget buat pemula!

Kelas Tutorial Next JS 15 Mengatur Website Agar Lebih SEO Friendly di BuildWithAngga

Tutorial Next JS 15 Mengatur Website Agar Lebih SEO Friendly

Kalau kamu sedang ingin membangun website modern untuk keperluan bisnis online, Next JS 15 adalah salah satu pilihan paling make sense di zaman sekarang. Kenapa? Karena framework ini dirancang bukan cuma untuk kemudahan pengembangan, tapi juga untuk performa dan visibilitas website kamu di mesin pencari seperti Google. Di era digital seperti sekarang, punya website aja gak cukup. Website kamu harus bisa ditemukan dengan mudah oleh calon pelanggan, apalagi kalau kamu jualan lewat landing page atau punya katalog produk online. Nah, di sinilah peran SEO (Search Engine Optimization) jadi sangat penting. Banyak bisnis gagal bersaing karena mereka fokus ke tampilan, tapi lupa optimasi struktur dan performa teknis yang disukai mesin pencari. Untungnya, Next JS datang membawa solusi lengkap: selain powerful buat developer, dia juga paunya fitur bawaan yang siap bantu kamu bersaing secara organik di Google. Tanpa perlu terlalu banyak konfigurasi ribet seperti zaman pakai framework lama, di Next JS semuanya sudah disiapkan: mulai dari struktur folder yang SEO-friendly, rendering strategi yang bisa disesuaikan, sampai dukungan built-in untuk metadata dan optimalisasi gambar. Semua ini bikin website kamu bisa lebih cepat di-load dan lebih mudah dipahami oleh crawler-nya Google. Next JS Punya Fitur-Fitur yang Memperkuat SEO Website Bisnis Salah satu hal keren dari Next JS adalah dia ngerti banget kebutuhan pebisnis online. Framework ini bukan cuma mikirin gimana developer bisa kerja cepat, tapi juga mikirin hasil akhirnya: website yang cepat, stabil, dan SEO-friendly. Misalnya, Next JS bisa membantu kita menerapkan teknik rendering yang sesuai dengan halaman yang kita buat. Untuk halaman produk atau landing page, kita bisa pakai static rendering supaya kecepatan maksimal dan bisa di-crawl dengan baik. Untuk halaman yang sering berubah datanya, kita bisa pakai server rendering atau ISR (Incremental Static Regeneration) supaya tetap optimal di mata mesin pencari. Selain itu, Next JS juga punya sistem penulisan metadata yang rapi. Kamu bisa atur title, description, dan tag lain secara fleksibel di setiap halaman. Ini penting banget buat SEO, karena search engine butuh informasi ini buat menampilkan konten kamu dengan baik di hasil pencarian. Dan masih banyak lagi fitur menarik lainnya yang akan kita pelajari bersama di artikel ini: mulai dari penggunaan image yang otomatis teroptimasi, struktur URL yang bersih, sampai integrasi dengan berbagai alat analitik. Kalau kamu serius ingin websitemu tampil di halaman pertama Google dan membantu bisnis makin dikenal orang, maka pelajari SEO lewat Next JS ini bisa jadi keputusan penting yang membuka banyak peluang baru. Yuk kita lanjut kae bagian berikutnya dan bahas satu per satu fitur yang bisa kamu manfaatkan. Tata Cara Menggunakan generateMetadata() untuk SEO pada Proyek Next JS Di Next JS 15 dengan App Router, pengaturan SEO seperti title dan description halaman sekarang jadi lebih rapi dan terstruktur. Semua itu bisa dilakukan dengan menggunakan fungsi generateMetadata() yang disediakan khusus untuk digunakan di setiap file page. Fungsi ini memungkinkan kita untuk mengatur metadata secara dinamis atau statis, tergantung dari jenis halaman dan kebutuhannya. Hal ini sangat berguna saat kita ingin mengoptimalkan SEO berdasarkan konten yang muncul di halaman tersebut, seperti judul artikel, nama produk, atau informasi kota tertentu. Berikut adalah contoh penggunaan generateMetadata() secara lengkap di halaman detail kota: // app/city/[slug]/page.tsx import { Metadata } from 'next' import { cities } from '@/data/cities.mock' type Props = { params: { slug: string } } export async function generateMetadata({ params }: Props): Promise<Metadata> { const city = cities.find((item) => item.slug === params.slug) if (!city) { return { title: 'City Not Found – Angga Office', description: 'Sorry, the city you are looking for does not exist.', } } return { title: `${city.name} Office Spaces – Angga Office`, description: `Explore premium office space and coworking in ${city.name}. Book your space today and grow your business with Angga Office.`, openGraph: { title: `${city.name} Office Spaces – Angga Office`, description: `Explore premium office space and coworking in ${city.name}.`, images: [ { url: city.thumbnail, width: 1200, height: 630, alt: `${city.name} Office`, }, ], }, } } export default function CityDetailPage({ params }: Props) { const city = cities.find((item) => item.slug === params.slug) if (!city) { return <div className="text-center py-20">City not found</div> } return ( <div className="max-w-4xl mx-auto py-10"> <h1 className="text-3xl font-bold mb-4">{city.name}</h1> <img src={city.thumbnail} alt={city.name} className="rounded-xl" /> <p className="mt-4 text-gray-600"> Discover the best office spaces available in {city.name}. Choose your ideal location and get started today. </p> </div> ) } Penjelasan singkatnya begini: kita memanfaatkan generateMetadata() untuk mengambil data berdasarkan params.slug, lalu mengembalikan objek metadata seperti title, description, dan openGraph. Dengan begini, setiap halaman kota punya metadata yang unik dan sesuai isi kontennya, yang tentunya sangat bagus untuk SEO. Oh ya, kalau kamu pakai Next Image dan punya thumbnail yang tajam dan cepat dimuat, itu juga bantu banget memperkuat nilai SEO dan user expeerience. Fitur ini bisa kamu terapkan di berbagai jenis halaman dinamis lain, seperti halaman produk, artikel blog, atau detail kursus. Jadi metadata kamu selalu relevan dan mendukung ranking pencarian organik. Tata Cara Menggunakan Open Graph (OG) untuk SEO pada Proyek Next JS Open Graph (OG) adalah standar metadata yang digunakan oleh media sosial seperti Facebook dan LinkedIn untuk menampilkan preview yang menarik saat halaman website kamu dibagikan. Nah, di Next JS 15 dengan App Router, kita bisa menyisipkan data OG ini langsung dari fungsi generateMetadata(). Cara penggunaannya hampir sama seperti metadata biasa, kita cukup tambahkan properti openGraph dalam return dari generateMetadata(). Biasanya OG berisi informasi seperti title, description, url, dan images. Berikut adalah contoh implementasi openGraph untuk halaman detail sebuah produk atau kota: // app/city/[slug]/page.tsx import { Metadata } from 'next' import { cities } from '@/data/cities.mock' type Props = { params: { slug: string } } export async function generateMetadata({ params }: Props): Promise<Metadata> { const city = cities.find((item) => item.slug === params.slug) if (!city) { return { title: 'City Not Found – Angga Office', description: 'The city you are looking for does not exist.', } } return { title: `${city.name} Office Spaces – Angga Office`, description: `Explore premium office spaces in ${city.name}. Boost your team's productivity today.`, openGraph: { title: `${city.name} Office Spaces – Angga Office`, description: `Explore premium office spaces in ${city.name}. Book your location today.`, url: `https://yourdomain.com/city/${city.slug}`, siteName: 'Angga Office', images: [ { url: city.thumbnail, width: 1200, height: 630, alt: `${city.name} Office Thumbnail`, }, ], locale: 'en_US', type: 'website', }, } } Dengan cara ini, saat pengguna share halaman tersebut di media sosial, akan muncul preview lengkap dengan gambar, judul, dan deskripsi yang sesuai — tidak cuma tampil teks acak dari halaman kamu. Ini sangat membantu meningkatkan engagemeent dari media sosial ke website kamu. Tata Cara Menambahkan Twitter Card Support untuk SEO pada Proyek Next JS Selain Open Graph, Twitter punya format sendiri untuk menampilkan preview saat link dibagikan, yaitu Twitter Card. Untungnya, Next JS juga mendukkung hal ini lewat properti twitter dalam fungsi generateMetadata(). Kamu bisa mengatur jenis kartu, gambar, dan keterangan yang akan muncul saat link halaman dibagikan di Twitter. Berikut contoh implementasinya: // app/city/[slug]/page.tsx import { Metadata } from 'next' import { cities } from '@/data/cities.mock' type Props = { params: { slug: string } } export async function generateMetadata({ params }: Props): Promise<Metadata> { const city = cities.find((item) => item.slug === params.slug) if (!city) { return { title: 'City Not Found – Angga Office', description: 'Sorry, city data is not available.', } } return { title: `${city.name} Office Spaces – Angga Office`, description: `Get access to professional office spaces in ${city.name} with top-class facilities.`, twitter: { card: 'summary_large_image', title: `${city.name} Office Spaces – Angga Office`, description: `Get access to premium coworking and office rentals in ${city.name}.`, site: '@AnggaOffice', creator: '@AnggaOfficial', images: [city.thumbnail], }, } } Dengan menambahkan dukungan Twitter Card, kamu memberikan kesan profesional dan siap bersaing di dunia digital. Ketika pengguna membagikan halaman kamu di Twitter, akan muncul gambar besar, judul, dan deskripsi yang kamu atur — bukan sekadar link polos. Dua kombinasi antara Open Graph dan Twitter Card ini merupakan strategi yang sangat kuat untuk meningkatkan klik dan daya tarik website kamu dari social media, yang tentunya sangat berpengaruh terhadap SEO dan traffic jangka panjang. Tata Cara Mengatur Sitemap dan robots.txt untuk SEO pada Proyek Next JS Supaya website kamu lebih cepat dikenali dan diindeks oleh Google maupun mesin pencari lainnya, kamu perlu menyiapkan dua hal penating: sitemap.xml dan robots.txt. Keduanya bekerja sama membantu crawler mesin pencari menavigasi struktur website kamu dengan benar dan efisien. Di Next JS, khususnya versi terbaru dengan App Router, kamu bisa menggunakan fitur bawaan untuk membuat file-file ini secara dinamis lewat app/ directory. Untuk membuat sitemap, kamu bisa membuat file sitemap.xml/route.ts di dalam folder app/. Ini akan menghasilkan endpoint /sitemap.xml secara otomatis. Kamu bisa buat list URL dari data statis atau dinamis, lalu tampilkan sebagai XML. Berikut contoh pengaturannya: // app/sitemap.xml/route.ts import { cities } from '@/data/cities.mock' export async function GET() { const baseUrl = '<https://buildwithangga.com>' const staticPages = ['', '/about', '/contact'].map((path) => { return `<url><loc>${baseUrl}${path}</loc></url>` }) const dynamicCityPages = cities.map((city) => { return `<url><loc>${baseUrl}/city/${city.slug}</loc></url>` }) const xml = ` <?xml version="1.0" encoding="UTF-8"?> <urlset xmlns="<http://www.sitemaps.org/schemas/sitemap/0.9>"> ${staticPages.join('')} ${dynamicCityPages.join('')} </urlset> `.trim() return new Response(xml, { headers: { 'Content-Type': 'application/xml', }, }) } Dengan file ini, saat kamu buka https://yourdomain.com/sitemap.xml, akan muncul semua URL penting dalam format yang bisa dipahami Googlebot. Selanjutnya untuk robots.txt, kamu bisa buat file robots.txt/route.ts agar otomatis tersedia di URL /robots.txt. File ini memberi tahu crawler halaman mana yang boleh atau tidak boleh diakses. Berikut contohnya: // app/robots.txt/route.ts export async function GET() { const content = ` User-agent: * Allow: / Sitemap: <https://yourdomain.com/sitemap.xml> `.trim() return new Response(content, { headers: { 'Content-Type': 'text/plain', }, }) } Di sini, User-agent: * artinya aturaen ini berlaku untuk semua bot. Allow: / berarti semua halaman diizinkan untuk di-crawl. Baris terakhir menunjukkan lokasi sitemap agar bot bisa mengaksesnya dengan mudah. Kombinasi sitemap.xml dan robots.txt ini sangat penting untuk SEO teknikal. Google akan lebih cepat memahami halaman apa saja yang penting di website kamu, dan menghindari halaman yang tidak perlu, sehingga meningkatkan performa indexing dan ranking halamanmu di hasil pencarian. 10 Kesalahan yang Dapat Merusak SEO pada Proyek Next JS Meskipun Next JS punya fitur-fitur bawaan yang sangat mendukung SEO, tetap ada banyak kesalahan umum yang bisa bikin ranking website kamu jeblok di hasil pencarian. Di bawah ini adalah daftar kesalahan penting yang perlu kamu hindari, lengkap dengan contoh kasus nyata dalam proyek Next JS. Tidak Mengatur Metadata Secara Dinamis di Halaman Dinamis Banyak developer hanya fokus mengatur metadata di homepage atau halaman statis saja. Padahal, halaman dinamis seperti detail produk atau artikel juga butuh metadata yang unik agar bisa tampil optimal di hasil pencarian. // SALAH: Metadata tidak disesuaikan dengan data konten export const metadata = { title: 'Produk - My Store Angga', description: 'Detail produk di toko kami.', } // BENAR: Gunakan generateMetadata untuk metadata yang dinamis export async function generateMetadata({ params }) { const product = await getProduct(params.slug) return { title: `${product.name} - My Store`, description: product.description, } } Tidak Menggunakan Semantic HTML seperti , , , dan Penggunaan tag HTML biasa seperti <div> tanpa struktur yang jelas bisa menyulitkan mesin pencari memahami hierarki konten halaman kamu. // SALAH return ( <div> <div>Ini judul</div> <div>Konten utama</div> </div> ) // BENAR return ( <main> <header> <h1>Ini judul</h1> </header> <article> <p>Konten utama</p> </article> </main> ) Mengabaikan Alt Text pada Gambar Gambar tanpa alt membuat aksesibilitas berkurang dan SEO jadi turun karena mesin pencari tidak bisa memahami isi gambar. // SALAH <Image src="/product.jpg" width={500} height={300} /> // BENAR <Image src="/product.jpg" alt="Gambar produk A" width={500} height={300} /> Tidak Mengatur Robots.txt dan Sitemap.xml Tanpa dua file ini, crawler Google bisa kesulitan menjelajahi halaman penting, atau malah mengindeks halaman yang tidak perlu seperti /admin atau /checkout. // Sitemap dan robots telah dijelaskan di jawaban sebelumnya Menggunakan Client Component untuk Halaman Informasi Statis Client Component butuh JavaScrript untuk render halaman, yang bisa menunda proses indexing oleh mesin pencari. // SALAH 'use client' export default function AboutPage() { return <div>Tentang Kami</div> } // BENAR export default function AboutPage() { return <div>Tentang Kami</div> } Konten Halaman Muncul Terlambat karena Fetching di Client Kalau data penting seperti deskripsi produk diambil di client, mesin pencari bisa gagal membaca kontennya sebelum rendering selesai. // SALAH 'use client' useEffect(() => { fetch('/api/product').then(setData) }, []) // BENAR export async function generateMetadata() { const product = await getProduct() return { title: product.name } } Tidak Mengatur Canonical URL Tanpa canonical tag, halaman dengaan konten yang sama bisa dianggap sebagai duplicate content dan bikin ranking turun. // Contoh metadata dengan canonical export async function generateMetadata({ params }) { return { title: 'Detail Produk', alternates: { canonical: `https://yourdomain.com/product/${params.slug}`, }, } } Menggunakan Gambar dari Sumber Eksternal Tanpa Konfigurasi Kalau kamu pakai <Image> dari Next untuk gambar eksternal tanpa izin domain di next.config.js, gambar tidak akan muncul dan SEO jadi buruk. // next.config.js images: { domains: ['external-image.com'], } Struktur URL Tidak Konsisten dan Tidak Ramah Mesin Pencari Gunakan URL yang bersih dan deskriptif. Hindari penggunaan query string acak atau angka ID saja. // SALAH: /product?id=12345 // BENAR: /product/tas-kulit-original Tidak Menambahkan Social Sharing Metadata (OG & Twitter Card) Kalau metadata OG dan Twitter Card tidak tersedia, halaman kamu terlihat membosankan saat dibagikan di media sosial, dan itu bisa menurunkan CTR (click-through rate). export async function generateMetadata() { return { title: 'Kelas BWA Terbaik', openGraph: { title: 'Produk Terbaik', description: 'Dapatkan produk terbaik di toko kami.', images: ['/product.jpg'], }, twitter: { card: 'summary_large_image', title: 'Produk Terbaik', images: ['/product.jpg'], }, } } Dengan menghindari 10 kesalahan ini, kamu bisa memaksimalkan potensi SEO dari website Next JS kamu. Mesin pencari akan lebih mudah memahami, merayapi, dan menampilkan konten kamu di hasil pencarian yang tepat. Penutup dan Saran untuk Web Developer Pemula Hingga Expert Belajar Next JS untuk SEO memang bukan hal yang instan, tapi percayalah — ini investasi jangka panjang yang akan sangat menguntungkan. Dengan menguasai praktik-praktik SEO seperti metadata, Open Graph, Twitter Card, sitemap, robots.txt, hingga struktur konten yang ramah crawler, kamu tidak hanya menjadi developer yang bisa bikin website, tapi developer yang ngerti strategi bisnis digital. Untuk kamu yang masih pemula atau sudah menengah tapi ingin naik level jadi web developer expert, saran terbaiknya: belajarlah langsung dari mentor yang sudah berpengalaman dan pernah menjalankan proyek nyata. Di BuildWithAngga, kamu bisa belajar bareng mentor expert yang akan bimbing kamu step-by-step, bukan cuma teori tapi langsung praktik di proyek profesional. Beberapa benefit unggulan yang kamu bisa dapatkan: Belajar dari studi kasus nyata, bukan hanya dari dokumentasiKelas berkualitas tinggi dengan sistem pembelajaran fleksibelAkses seumur hidup ke materi dan update terbaruKomunitas aktif untuk diskusi dan portfolio reviewSertifikat yang bisa menunjang karir profesional dan freelance Dan yang paling penting: semua ini didesain untuk mempersiapkan kamu bekerja secara remote. Artinya kamu bisa kerja dari mana aja, gabung di perusahaan internasional, atau jadi freelancer yang dibayar mahal — asalkan kamu punya skill dan mindset yang benar. Yuk mulai upgrade dirimu hari ini. Jangan cuma jadi web developer biasa. Jadi developer yang dicari karena bisa bangun website yang cepat, scalable, dan siap masuk halaman satu Google. Belajar bareng mentor expert di BuildWithAngga sekarang dan siapkan diri kamu buat karir digital yang sukses. 🚀

Kelas Challenge Praktek Slicing Tailwind CSS Tanpa Takut Salah (Best Practice) di BuildWithAngga

Challenge Praktek Slicing Tailwind CSS Tanpa Takut Salah (Best Practice)

Daftar Isi PendahuluanDownload Desain di ShaynakitDownload Source Code di Shaynakit (Disarankan Download Ketika Sudah Membaca/Praktek Semua Isi Artikelnya)Baca Desain DuluWarnaGambarIconPolaLogika AksiPindah HalamanJudulBagian-BagianPemanasanInstall Tailwind CSSImport Tailwind in your CSSStart using Tailwind in your HTMLStart the Tailwind CLI build processMenerapkanSet Up Desain MobileTopicsMost PopularPage SelanjutnyaPenutup Pendahuluan Kadang kamu suka mikir “Sudah/Mau praktek Tailwind CSS ah, sudah bisa sih gitu-gitu aja, tapi apa emang bener? masak sih enggak ada aturan yang bikin Tailwind CSS ini Sesuai di Kerjaan? ”Di latihan ini, kita gak mulai dari nol, desainnya udah ada, tinggal gimana caranya kamu “tantang” desain itu jadi halaman web beneran pakai Tailwind CSS. Anggap aja kayak lagi dapet puzzle yang gambarnya udah kelihatan di kotaknya. Tantangannya tinggal tantang dirimu nyusun potongan-potongan kecilnya (HTML + Tailwind class) biar pas dan hasil akhirnya mirip sama gambar aslinya. Tenang hasil benernya tetep saya kasih tau. Tujuan dari latihan ini adalah biar menantang kamu makin terbiasa slicing dari desain ke kode, memahami struktur layout, warna, hingga spacing, dan nerapin semuanya dengan utilitas Tailwind yang cepat dan efisien. Gak cuma teori, tapi langsung praktik biar skill-nya nempel. Siap? Yuk kita mulai nyusun puzzlenya! 🧩💻✨ Download Desain di Shaynakit Untuk mulai melakukan challenge desain Bookies Read Digital Book App, Kamu perlu mengunduh file desain Figma-nya terlebih dahulu. Desain ini tersedia secara gratis dalam mode Free Trial melalui situs ShaynaKit. Berikut adalah langkah-langkah lengkapnya: Buka situs utama ShaynaKit di https://shaynakit.com/landing.Klik menu atau tombol Register atau langsung buka https://shaynakit.com/register untuk membuat akun terlebih dahulu.Setelah berhasil mendaftar dan login, buka halaman desain yang akan digunakan di https://shaynakit.com/details/bookies-read-digital-book-app.Klik tombol Download, lalu pada opsi yang tersedia, pilih Free Trial.Klik tombol Start Today untuk memulai akses gratis.Setelah itu, kembali ke halaman yang sama: https://shaynakit.com/details/bookies-read-digital-book-app, dan klik tombol Download sekali lagi.File desain akan terunduh dalam format .fig, yaitu format asli file Figma.Simpan file .fig tersebut ke dalam folder lokal proyek Anda, misalnya ./figma/BookiesReadDigitalBookApp.fig. Download Source Code di Shaynakit (Disarankan Download Ketika Sudah Membaca/Praktek Semua Isi Artikelnya) Jadi Shaynakit udah nyiapin source code desainnya buat kita. Isinya macem-macem, ada icon, gambar, pokoknya semua yang kita butuhin buat ngebangun halaman nanti. Tapi nih ya, saran aku: lewatkan dulu bagian ini. Santai dulu, baca aja dulu semua artikelnya sampai beres biar gak kehilangan rasa challenge nya. Ibaratnya kayak dapet bahan masakan lengkap dari chef, tapi sebelum mulai masak, mending kita baca dulu resepnya sampai akhir biar gak asal campur dan hasilnya tetep enak pas disajiin. 😄 Kalau udah paham alurnya, baru deh balik lagi ke sini buat download source-nya. Nah, sekarang kita liat dulu gimana caranya ngedownload file-nya, jadi kayak gini caranya cukup ikutin langkah-langkah ini: Buka situs utamanya di sini: https://shaynakit.com/landing.Klik menu Register atau langsung aja meluncur ke https://shaynakit.com/register buat daftar akun dulu.Setelah berhasil daftar dan login, buka halaman template yang mau dipakai di sini: https://shaynakit.com/details/bookies-read-digital-html-tailwind-css-template.Klik tombol Download, lalu pilih opsi Free Trial.Lanjut klik Start Today buat mulai akses gratisnya.Setelah itu, balik lagi ke halaman template tadi dan klik tombol Download sekali lagi.File-nya bakal langsung keunduh dalam format .zip.Simpan file .zip itu ke folder lokal kamu, misalnya ke: ./source-code/Bookies.zip. Baca Desain Dulu Nah, setelah mendownload desain yang ada di Shaynakit, selanjutnya kita bakal nyoba baca desainnya, yaitu dimulai dari hal-hal dasar yang sering disepelekan tapi penting banget, kayak: Warna Grey Light (#F6F8FA) Warna yang satu ini dipakai buat background card. Kelihatannya sepele, tapi sebenarnya penting banget karena sering muncul juga di bagian-bagian lain desainnya. Jadi pas baca desain, jangan skip bagian ini ya, perhatiin bener-bener. Yuk, langsung kita lihat gambar di bawah ini: Hover Grey Light (#eaeef2) Warna yang ini emang nggak ada di desain aslinya, karena aku tambahin sendiri biar ada interaksi yang lebih hidup pas card-nya di-hover. Coba bayangin deh, waktu kursor nge-hover ke card terus background-nya berubah, otomatis kita bakal ngerasa, “Eh, ini bisa diklik nih!” Nah, efek-efek kecil kayak gini tuh ngaruh banget ke pengalaman pengguna. Dan karena warna ini frekuensi penggunaannya mirip kayak warna utama di atas, kita juga harus anggap ini penting. Jadi nanti, pas hover, background-nya bakal kayak gini: Grey(#90909E) Nah, kalau yang ini juga sama, sering banget dipakai di berbagai elemen. Jadi, pas baca desain, kita harus anggap ini penting juga. Jangan sampai kelewat, karena warna ini bantu ngejaga konsistensi dan nuansa keseluruhan desain. Warna kayak warna teks ini: Black(#35325E) Warna yang ini juga sama, sering banget kepake. Contohnya, bisa kamu lihat di teks yang tulisannya “Topics”. Karena sering muncul di halaman halaman, kita perlu perhatiin juga pas baca desainnya, biar nanti pas ngoding lebih gampang. Button Purple(#57549E) Warna ungu yang satu ini dipakai buat tombol. Nah, biasanya nih, warna tombol itu bakal konsisten dipakai di semua halaman yang punya tombol juga. Jadi penting banget buat diinget! Hal-hal kayak gini jangan disepelein, karena warna tombol itu ngaruh ke identitas dan interaksi di seluruh halaman. Jadi pastiin kamu catat warna ini sebagai salah satu warna utama yang harus diingat pas baca desain kayak gini: Border Grey(#E9E8ED) Warna Grey ini dipakai buat bagian cincin di tombol love, yang bikin tombolnya kelihatan punya outline atau border gitu. Nah, warna ini sering banget dipake juga buat tombol-tombol yang isinya cuma icon, kayak gambar di bawah ini kalau pagenya membesar atau kamu tambahin biar menjadikan brand website kamu yang konsisten. Karena sering muncul di elemen kecil tapi penting, kita juga harus anggap warna ini penting pas baca desain. Jangan sampai kelewat ya! Gambar Nah, kalau urusan gambar sih simpel aja. Intinya, kalau dia bukan icon, ya berarti itu gambar. Sesimpel itu. 😄 Contohnya nih, yang kayak gini yang disebut gambar: Icon Sekarang lanjut ke icon. Kalau icon itu identik sama yang kecil-kecil, bentuknya unik-unik, dan biasanya dipakai buat bantuin komunikasi visual, kayak nunjukin aksi atau makna tertentu. Ya semacam logo love, panah, bintang, setting, dan teman-temannya gitu lah. 😄 Biasanya juga ukurannya kecil, tapi perannya gede banget buat ngarahin atau ngasih kesan interaktif ke user kayak ini: Pola Selanjutnya kita bahas pola nih, ini juga penting banget buat kamu perhatiin. Kalau aku sih, pola ini kayak trik rahasia supaya kode kamu nanti jadi gampang dibaca dan rapi dari awal. Misalnya nih, kamu punya banyak card yang jarak antar card-nya sama rata. Nah, kalau kamu perhatiin pola ini dari awal, pas nanti pakai Tailwind CSS, tinggal kasih utility kayak flex sama gap-[…], beres deh! Jarak antar card langsung rapi dan konsisten tanpa ribet. Ini gak cuma bikin hidup kamu lebih gampang, tapi juga bikin kolaborasi sama tim jadi mulus karena kode kamu jadi lebih terstruktur dan “advance”. Kalau kamu baca di desain, ini yang termasuk pola: Logika Aksi Nah, untuk bagian ini, kita juga harus jeli baca desain dan mulai mikir, “Ini kira-kira butuh JavaScript atau enggak ya buat interaksinya?” Biar dari awal kita jelas, mana yang harus pakai JS dan mana yang cukup pakai Tailwind CSS doang. Nah, di bawah ini contohnya: Gambar pertama itu JS-nya dipakai buat bikin scroll horizontal yang mulus banget, jadi pengguna bisa geser-geser dengan nyaman. Kalau gambar kedua, JS-nya dipakai supaya pas diklik, isi warnanya di icon love-nya berubah, jadi keliatan interaktif dan responsif. Jadi, bagian-bagian kayak gini wajib kita perhatiin biar kode kita nggak asal comot tapi memang pas dan tepat fungsi. Pindah Halaman Nah, untuk yang ini, kita juga harus peka pas baca desain “Mana sih elemen yang bisa bikin kita pindah halaman kalau diklik?” Ini penting banget, soalnya nanti tag HTML-nya beda, gak cuma section atau div biasa, tapi harus pakai tag <a>. Tag <a> ini fungsinya buat link, alias buat pindah halaman. Jadi, kalau ada elemen yang fungsinya bawa kita ke halaman lain, wajib pakai ini. Nah, contoh elemen yang biasanya dipakai buat pindah halaman bisa kamu lihat di gambar bawah ini: Judul Nah, yang ini juga penting kita perhatiin, yaitu mana yang judul, dan mana yang deskripsi. Intinya sih, selain judul, pasti deskripsi yang menjelasin lebih detail. Nanti pas ngoding, kita bakal pakai tag <h> buat judul, dan tag <p> buat paragraf atau deskripsi. Dengan baca desain dari awal kayak gini, kode kita jadi lebih semantic dan pastinya juga ramah buat SEO. Jadi, selain enak buat developer, juga bikin website kita lebih gampang ditemukan di mesin pencari. Contohnya judul kayak ini semua: Bagian-Bagian Nah, ini juga penting banget buat kita perhatiin di desain, karena ini nunjukin bagian-bagian apa aja yang ada di satu halaman. Setiap bagian nanti kita bungkus pakai tag <section>. Tujuannya simpel: biar kodenya lebih mudah dibaca, lebih semantic, dan pastinya lebih SEO-friendly juga. Jadi, selain rapi, struktur halaman kita juga jadi jelas dan gampang dipahami baik oleh developer maupun mesin pencari. Mantap kan. Topics Di halaman pertama, ternyata ini bisa dibilang sebagai satu bagian yang utuh, karena terdiri dari satu judul utama yang paling menonjol jadi bakal kita bungkus pake section, kayak gini nih: Most Popular Terus, lanjut ke bagian Most Popular juga, karena posisinya setara sama bagian Topics. Jadi dua-duanya kita anggap sebagai bagian penting yang terpisah dalam halaman ini. Pemanasan Sekarang kita mulai masuk ke tahap ngoding nih. Tapi sebelum langsung tancap gas, kita pemanasan dulu ya, kayak stretching sebelum lari. Di sini kita bakal download dulu kebutuhan dasar buat ngoding pake Tailwind CSS. Ibaratnya, ini tuh kayak nyiapin kompor dan alat masak sebelum mulai masak. Jadi, langkah-langkah awalnya kira-kira kayak gini nih: Install Tailwind CSS Jalankan kode ini npm install tailwindcss @tailwindcss/cli Perintah npm install tailwindcss @tailwindcss/cli ini buat install Tailwind CSS sama tool CLI-nya di proyek kamu. Jadi, Tailwind CSS itu framework yang bikin styling lebih gampang pakai kelas-kelas siap pakai, terus CLI-nya yang ngebantu nge-compile file CSS biar siap dipakai di web. Intinya, ini langkah pertama supaya kamu bisa mulai ngoding pake Tailwind dengan lancar dan simpel. Import Tailwind in your CSS Lanjutin ini src/input.css @import "tailwindcss"; Kalau @import "tailwindcss"; ini fungsinya buat masukin semua style bawaan Tailwind ke file CSS kamu. Jadi nanti kamu bisa langsung pakai semua utility class yang disediain Tailwind tanpa harus nulis CSS dari nol. Simple banget, tinggal panggil ini di file utama CSS kamu, terus tinggal mulai styling! Start using Tailwind in your HTML lanjutin ini src/index.html <!doctype html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link href="./output.css" rel="stylesheet"> </head> <body> <h1 class="text-2xl font-extrabold"> BuildWithAngga </h1> </body> </html> Disini Tailwind udah siap pakai. Start the Tailwind CLI build process Lanjutin ini npx @tailwindcss/cli -i ./src/input.css -o ./src/output.css --watch ini buat ngejalanin Tailwind CLI supaya dia ngubah file CSS kamu yang ada di input.css jadi file siap pakai di output.css. Kalau ada perubahan di input.css, karena ada --watch, proses kompilasi bakal jalan terus-menerus otomatis tanpa kamu harus jalanin perintah lagi. Jadi kamu bisa langsung lihat perubahan di browser waktu kamu ngoding. Menerapkan Kalau Tailwind CSS-nya udah beres kita setup, sekarang saatnya kita masuk ke bagian seru, nerapin desain ke dalam kode. Ibaratnya, alat tempur udah siap, sekarang kita mulai ngerakit satu per satu elemennya sesuai blueprint. Yuk, mari kita gas pelan-pelan tapi pasti! 💻🔥 Set Up Desain Mobile Kode Saya <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="description" content="" /> <meta name="theme-color" content="#1B4DFF" /> <!-- Favicon --> <link rel="icon" href="./assets/images/logos/company.svg" /> <link rel="apple-touch-icon" href="./assets/images/logos/company.svg" /> <!-- Preload key resources --> <link rel="preconnect" href="<https://fonts.googleapis.com>" /> <link rel="preconnect" href="<https://fonts.gstatic.com>" crossorigin /> <link rel="preconnect" href="<https://cdn.jsdelivr.net>" crossorigin /> <!-- Stylesheets --> <link href="./output.css" rel="stylesheet" /> <link href="<https://fonts.googleapis.com/css2?family=Poppins:wght@100;200;300;400;500;600;700;800;900&display=swap>" rel="stylesheet" /> <link rel="stylesheet" href="<https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css>" /> <title>Bookies | Home</title> </head> <body> <main class="relative mx-auto w-full max-w-[640px] overflow-hidden min-h-screen pt-[40px] pb-[43px] flex flex-col gap-[30px] bg-white"> </main> <script src="<https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js>"></script> <script src="./js/swiper.js"></script> </body> </html> Alasannya yaitu : Bagian <link href="<https://fonts.googleapis.com/css2?family=Poppins:wght@100;200;300;400;500;600;700;800;900&display=swap>" rel="stylesheet" /> berfungsi untuk memuat font Poppins dari Google Fonts dengan berbagai tingkat ketebalan, supaya tampilan teks di halaman lebih keren dan konsisten; link stylesheet Swiper di <link rel="stylesheet" href="<https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css>" /> buat styling slider agar tampilannya rapi dan fungsional; sedangkan <title>Bookies | Home</title> adalah judul halaman yang muncul di tab browser. Di bagian <main class="relative mx-auto w-full max-w-[640px] overflow-hidden min-h-screen pt-[40px] pb-[43px] flex flex-col gap-[30px] bg-white"> ini adalah wadah utama halaman dengan styling layout responsif dan rapi untuk versi mobile, jadi versi mobile itu max 640px, lalu di bawah ada dua script yaitu Swiper JS dari CDN yaitu <script src="<https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js>"></script> dan file lokal swiper.js yang berisi konfigurasi slider agar interaktif. @import "tailwindcss"; body { font-family: "Poppins"; background-color: #DAE1E9; } @theme { --color-bookies-grey-light: #F6F8FA; --color-bookies-hover-grey-light: #eaeef2; --color-bookies-grey: #90909E; --color-bookies-black: #35325E; --color-bookies-button-purple: #57549E; --color-bookies-border-grey: #E9E8ED; } Alasannya yaitu : Di bagian ini saya bikin konfigurasi awal buat warna dan font biar bisa dipakai konsisten di seluruh halaman. Nah, untuk tema warna itu saya ambil dari yang kita perhatiin di desain, semua warna penting yang perlu diingat saya tulis lengkap di sini supaya gampang pas dipakai atau nanti kalau mau diubah-ubah juga nggak ribet. Jadi intinya, ini jadi semacam “bank warna dan font” yang bikin styling lebih teratur dan efisien. Coba Kamu Coba dulu jangan langsung lihat kode saya, coba bikin sendiri dulu buat latihan challenge-nya. Kalau kamu udah mentok atau merasa udah kelar, baru deh intip kode saya. Dengan cara ini, kamu bisa tahu seberapa jago skill slicing kamu, plus tekanan dari challenge-nya bakal bikin pemahaman Tailwind CSS kamu makin naik level. Topics Kode Saya <section id="Topics" class="relative flex flex-col gap-3"> <h2 class="text-xl leading-[30px] text-bookies-black font-medium px-6">Topics</h2> <div class="Swiper w-full overflow-x-hidden"> <div class="swiper-wrapper py-[3px] "> <div class="swiper-slide !w-fit"> <a href="details.html"> <div class="flex flex-col gap-[35px] w-[138px] p-[18px] rounded-[25px] bg-[#8EC9F5] hover:ring-2 hover:ring-offset-1 hover:ring-[#80b2d9] transition-all duration-300"> <div class="rounded-full p-[11px] shrink-0 bg-[#C5E5FF] w-fit"> <img src="assets/images/icons/award.svg" alt="icon" class="size-6 shrink-0"> </div> <div class="flex flex-col gap-[6px]"> <h3 class="line-clamp-2 hover:line-clamp-none font-medium text-sm leading-[21px] text-bookies-black">My Self Improvement</h3> <p class="text-[10px] leading-[15px] text-[#3C6A8C]">84 books</p> </div> </div> </a> </div> <div class="swiper-slide !w-fit"> <a href="details.html"> <div class="flex flex-col gap-[35px] w-[138px] p-[18px] rounded-[25px] bg-[#ADAEFF] hover:ring-2 hover:ring-offset-1 hover:ring-[#9b9de6] transition-all duration-300"> <div class="rounded-full p-[11px] shrink-0 bg-[#D8D8FF] w-fit"> <img src="assets/images/icons/trending-up.svg" alt="icon" class="size-6 shrink-0"> </div> <div class="flex flex-col gap-[6px]"> <h3 class="line-clamp-2 hover:line-clamp-none font-medium text-sm leading-[21px] text-bookies-black">Business Education</h3> <p class="text-[10px] leading-[15px] text-[#3C6A8C]">12 books</p> </div> </div> </a> </div> <div class="swiper-slide !w-fit"> <a href="details.html"> <div class="flex flex-col gap-[35px] w-[138px] p-[18px] rounded-[25px] bg-[#FDEBEA] hover:ring-2 hover:ring-offset-1 hover:ring-[#e3d4d2] transition-all duration-300"> <div class="rounded-full p-[11px] shrink-0 bg-[#FFF8F8] w-fit"> <img src="assets/images/icons/star.svg" alt="icon" class="size-6 shrink-0"> </div> <div class="flex flex-col gap-[6px]"> <h3 class="line-clamp-2 hover:line-clamp-none font-medium text-sm leading-[21px] text-bookies-black">Non-Fiction Stories</h3> <p class="text-[10px] leading-[15px] text-[#3C6A8C]">5 books</p> </div> </div> </a> </div> </div> </div> </section> Alasannya yaitu : Saya pakai <section> karena saya yakin ini adalah bagian yang bisa berdiri sendiri, dan supaya gampang dikenali, saya kasih ID yang konsisten dan jelas. Untuk kelas swiper, itu memang bawaan dari Swiper JS, jadi saya pilih cara simpel pakai class bawaan itu supaya lebih mudah. Kalau kamu punya cara lain, silakan aja, nggak masalah. Struktur tagnya juga saya jaga rapi dan konsisten, misalnya semua judul saya pakai <h3>, terus yang bisa klik dan pindah halaman saya kasih tag <a>, paragraf saya tempatkan di <p>, dan icon saya beri kelas shrink-0 supaya ukurannya tetap pas dan nggak berantakan saat layar hape diperkecil. js/swiper.js var categoriesSwiper = new Swiper(".Swiper", { slidesPerView: "auto", spaceBetween: 16, slidesOffsetAfter: 24, slidesOffsetBefore: 24, }); Alasannya yaitu : Kalau yang ini kode JS-nya, fungsinya buat ngejalanin swiper biar slider-nya bisa swipe dengan mulus. Kalau kamu perhatiin, di kodenya itu ada pengaturan buat kasih celah kosong kiri dan kanan sebesar 24px, dan jarak antar cardnya 16px. Semua setting ini diambil dari elemen HTML yang punya class swiper, jadi nanti spacing-nya pas dan rapi sesuai desain. Coba Kamu Coba dulu jangan langsung lihat kode saya, coba bikin sendiri dulu buat latihan challenge-nya. Kalau kamu udah mentok atau merasa udah kelar, baru deh intip kode saya. Dengan cara ini, kamu bisa tahu seberapa jago skill slicing kamu, plus tekanan dari challenge-nya bakal bikin pemahaman Tailwind CSS kamu makin naik level. Most Popular Kode Saya <section id="MostPopular" class="flex flex-col gap-3 px-4"> <h2 class="text-xl leading-[30px] text-bookies-black font-medium">Most Popular</h2> <div class="flex flex-col gap-4"> <a href="details.html"> <div class="card rounded-[25px] p-4 flex items-end justify-between bg-bookies-grey-light hover:bg-bookies-hover-grey-light transition-all duration-300"> <div class="flex items-center gap-4"> <div class="p-1 rounded-[13px] w-[100px] h-[130px] bg-white shrink-0"> <div class="flex items-center justify-center rounded-[13px] overflow-hidden"> <img src="assets/images/thumbnails/most-popular-1.png" alt="thumbnails" class="w-full h-full object-cover"> </div> </div> <div class="flex flex-col gap-2"> <h3 class="font-medium text-bookies-black">Lamp of Brightnes Real World</h3> <p class="text-sm leading-[21px] text-bookies-grey">Growth Business</p> </div> </div> <button type="button" class="cursor-pointer"> <div class="size-[35px] shrink-0 rounded-full bg-white flex justify-center items-center"> <img src="assets/images/icons/love.svg" alt="icon" class="size-4"> </div> </button> </div> </a> <a href="details.html"> <div class="card rounded-[25px] p-4 flex items-end justify-between bg-bookies-grey-light hover:bg-bookies-hover-grey-light transition-all duration-300"> <div class="flex items-center gap-4"> <div class="p-1 rounded-[13px] w-[100px] h-[130px] bg-white shrink-0"> <div class="flex items-center justify-center rounded-[13px] overflow-hidden"> <img src="assets/images/thumbnails/most-popular-2.png" alt="thumbnails" class="w-full h-full object-cover"> </div> </div> <div class="flex flex-col gap-2"> <h3 class="font-medium text-bookies-black">Art of Gathering Do Meeting</h3> <p class="text-sm leading-[21px] text-bookies-grey">Team Product</p> </div> </div> <button type="button" class="cursor-pointer"> <div class="size-[35px] shrink-0 rounded-full bg-white flex justify-center items-center"> <img src="assets/images/icons/love.svg" alt="icon" class="size-4"> </div> </button> </div> </a> <a href="details.html"> <div class="card rounded-[25px] p-4 flex items-end justify-between bg-bookies-grey-light hover:bg-bookies-hover-grey-light transition-all duration-300"> <div class="flex items-center gap-4"> <div class="p-1 rounded-[13px] w-[100px] h-[130px] bg-white shrink-0"> <div class="flex items-center justify-center rounded-[13px] overflow-hidden"> <img src="assets/images/thumbnails/most-popular-3.png" alt="thumbnails" class="w-full h-full object-cover"> </div> </div> <div class="flex flex-col gap-2"> <h3 class="font-medium text-bookies-black">Tiger In The Garden For Eating</h3> <p class="text-sm leading-[21px] text-bookies-grey">Children Story</p> </div> </div> <button type="button" class="cursor-pointer"> <div class="size-[35px] shrink-0 rounded-full bg-white flex justify-center items-center"> <img src="assets/images/icons/love.svg" alt="icon" class="size-4"> </div> </button> </div> </a> </div> </section> Alasannya yaitu : Yang ini juga sama, aku coba terapkan judulnya pakai tag yang tepat biar semantik dan gampang dipahami, terus tombol lovenya aku kasih tag button karena dia ada logika interaksi, jadi memang pas pakai button. Di situ juga aku pakai h2 buat judul utama dan h3 buat sub judulnya. Nah, karena card ini bisa diklik buat pindah ke halaman detail, aku bungkus semuanya pakai tag a supaya fungsinya sebagai link jelas dan sesuai standar. Coba Kamu Coba dulu jangan langsung lihat kode saya, coba bikin sendiri dulu buat latihan challenge-nya. Kalau kamu udah mentok atau merasa udah kelar, baru deh intip kode saya. Dengan cara ini, kamu bisa tahu seberapa jago skill slicing kamu, plus tekanan dari challenge-nya bakal bikin pemahaman Tailwind CSS kamu makin naik level. Page Selanjutnya Untuk halaman selanjutnya, giliran kamu yang ambil alih! Setelah bareng-bareng kita ngebedah desain dan ngoding bareng, sekarang aku kasih kamu challenge yang sedikit lebih naik level. Kali ini kamu bakal eksplorasi sendiri, tanpa panduan langsung dari aku. Tapi tenang, kamu gak bener-bener sendirian kok, source code-nya udah aku siapin dan bisa kamu temuin di bab “Download Source Code di Shaynakit (Disarankan Download Ketika Sudah Membaca/Praktek Semua Isi Artikelnya)” (ingat, disarankan download-nya setelah baca/praktek semua isi artikelnya ya). Anggap aja ini kayak lepas roda bantu, udah waktunya kamu ngebut sendiri! Selamat eksplorasi, dan jangan takut buat eksperimen. Penutup Nah, sampai sini dulu perjalanan kita dari liat-liat desain, siapin alat tempur (alias Tailwind CSS), sampai akhirnya nyusun kode kayak nyusun lego sesuai blueprint-nya. Anggap aja tadi kita lagi masak bareng, awalnya kita baca resep dulu (desainnya), terus belanja bahan-bahannya (install Tailwind), baru deh kita mulai masak step by step (ngoding). Dan sekarang, menunya udah jadi! 🍽️ Selanjutnya tinggal kamu eksplorasi sendiri: tambahin bumbu, kreasikan gaya, atau bahkan bikin resep (desain) versi kamu sendiri. Intinya, makin sering latihan, makin jago juga kamu nanti. Sampai jumpa di masakan alias project berikutnya. Selamat ngoding, chef frontend! 👨‍🍳👩‍🍳✨

Kelas Mengenal Komponen di React JS: Functional vs Class di BuildWithAngga

Mengenal Komponen di React JS: Functional vs Class

Jika kamu baru mulai belajar React JS, kamu pasti akan sering mendengar istilah komponen. Komponen adalah bagian paling dasar dan penting dalam membangun antarmuka pengguna (UI) di React. Bisa dibilang, seluruh aplikasi React adalah kumpulan dari banyak komponen yang bekerja bersama-sama. Nah, di React sendiri ada dua cara utama untuk membuat komponen: Functional Component dan Class Component. Keduanya bisa digunakan untuk tujuan yang sama, tapi punya gaya penulisan, fitur, dan cara kerja yang sedikit berbeda. Pada awalnya, Class Component dianggap lebih powerful karena bisa mengelola state dan menggunakan lifecycle methods. Tapi sejak diperkenalkannya React Hooks pada versi 16.8, Functional Component pun jadi sangat fleksibel—bahkan kini lebih direkomendasikan dalam banyak kasus. Di artikel ini, kita akan: Mengenal kedua jenis komponen dengan penjelasan dan contoh kode.Membahas perbedaan utama antara Functional dan Class Component.Menjelaskan kesalahan umum yang sering terjadi saat menggunakannya.Memberikan beberapa best practice agar kamu bisa menulis komponen React dengan lebih baik. Jadi, kalau kamu masih bingung: “Harus pakai yang mana, Functional atau Class?”, tenang… kita bahas satu per satu dengan contoh nyata. Persiapan Proyek dan Cara Instalasi Sebelum kita mulai ngulik komponen di React, tentu kita perlu siapkan dulu proyek React nya, biar bisa langsung praktek dan nggak cuma teori doang. Syarat Awal Pastikan kamu udah install Node.js di komputer kamu, minimal versi 14 ya. Buat ngecek, buka terminal atau command prompt terus ketik: node -v npm -v Ini versi yg saya gunakan: Terminal Windows Kalau keluar versinya berarti udah oke, kalo belum, kamu bisa download Node.js dari situs resmi nodejs.org. Instalasi React dengan Next.js Nah, buat mulai proyek React, kita bakal pake Next.js. Next.js ini keren banget karena selain React biasa, dia juga punya fitur kayak routing otomatis dan server-side rendering yang bikin performa aplikasi makin ngebut. Buka terminal terus ketik perintah ini buat bikin proyek baru: npx create-next-app@latest bwa-react Jika ada konfirmasi seperti ini tekan Enter di keyboard Terminal: Install Next.js √ Would you like to use TypeScript? ... Pilih Yes√ Would you like to use ESLint? ... Pilih Yes√ Would you like to use Tailwind CSS? ... Pilh Yes√ Would you like your code inside a src/ directory? ... Pilih Yes√ Would you like to use App Router? (recommended) ... Pilih Yes√ Would you like to use Turbopack for next dev? ... Pilih Yes√ Would you like to customize the import alias (@/* by default)? ... Pilih No Terminal: Install Next.js Jika proses intallasi sudah selesai, masuk ke folder proyeknya: cd bwa-react Terus jalankan development server dengan perintah: npm run dev Terminal: Menjalankan Next.js Biasanya bakal keluar alamat http://localhost:3000 di terminal. Kamu buka alamat itu di browser, dan… voila! Kamu udah punya proyek React dengan Next.js siap pakai. Tampilan awal Next.js Kalau kamu pake bun, tinggal ganti aja npm jadi bun di perintah-perintah tadi. Mengenal Komponen di React Oke, sekarang saatnya masuk ke inti dari pembahasan kita: komponen. Jadi, di React, semua hal yang tampil di layar itu sebenernya dibangun dari komponen. Mulai dari tombol, teks, gambar, sampai halaman penuh semuanya komponen. Apa itu Komponen? Komponen itu bisa dibilang kayak "blok bangunan" dari UI aplikasi kita. Dia bisa dipake ulang, bisa dikasih data, dan bisa saling terhubung satu sama lain. Ibaratnya kayak LEGO, kamu gabungin banyak potongan kecil jadi satu bentuk utuh. Ada dua jenis utama komponen di React: Functional Component (alias komponen fungsi)Class Component (alias komponen kelas) Keduanya bisa dipake buat hal yang sama, tapi punya gaya nulis yang beda dan cara kerja yang sedikit… ya beda dikit lah. Functional Component Functional Component ditulis sebagai fungsi JavaScript biasa. Sejak adanya Hooks, Functional Component bisa ngelakuin hampir semua hal yang dulu cuma bisa di Class Component. Contoh sederhana: // components/HelloFunctional.tsx type HelloProps = { name: string; }; function HelloFunctional(props: HelloProps) { return <h1>Halo, {props.name}!</h1>; } export default HelloFunctional; Atau bisa juga pake arrow function (lebih modern): // components/HelloFunctionalArrow.tsx type HelloProps = { name: string; }; const HelloFunctional: React.FC<HelloProps> = ({ name }) => { return <h1>Halo, {name}!</h1>; }; export default HelloFunctional; Class Component Dulu, sebelum Hooks muncul, kalo kamu mau pake state atau lifecycle method, kamu harus pake Class Component. Contoh: // components/HelloClass.tsx import { Component } from 'react'; type HelloProps = { name: string; }; class HelloClass extends Component<HelloProps> { render() { return <h1>Halo, {this.props.name}!</h1>; } } export default HelloClass; Tapi sekarang, Class Component udah jarang dipake di proyek baru. Bukan berarti dilarang ya, cuma tren-nya udah beralih ke Functional Component. Perbandingan Singkat FiturFunctional ComponentClass ComponentPenulisanLebih ringkas & simpleLebih verboseState & lifecyclePakai HooksPakai this.state & this.setStateKode reuseLebih mudah dengan HooksLebih terbatasPopularitas sekarangSangat direkomendasikanMasih dipakai di proyek lamaTabel perbandingan functional component dan class component Masih bingung pilih yang mana? Tenang, kita bakal bahas lebih lanjut soal perbedaan dan kapan sebaiknya pake yang mana. Perbedaan Utama Functional vs Class Component Meski sama-sama bisa dipake buat bikin tampilan, ada beberapa perbedaan penting antara Functional Component dan Class Component. Biar nggak bingung, kita bahas satu-satu ya. Penulisan (Sintaks) Functional Component itu lebih ringkas. Cukup nulis fungsi, langsung return elemen JSX. // Functional const Hello = () => <h1>Hello, World!</h1>; Bandingin sama Class Component: // Class class Hello extends React.Component { render() { return <h1>Hello, World!</h1>; } } Liat kan? Class Component butuh lebih banyak baris buat hal yang sebenernya sama aja. Mengelola State Functional Component sekarang udah bisa pake Hooks buat ngatur state. import { useState } from 'react'; const Counter = () => { const [count, setCount] = useState(0); return ( <div> <p>Jumlah: {count}</p> <button onClick={() => setCount(count + 1)}>Tambah</button> </div> ); }; export default Counter; Dulu (dan masih bisa), kalo mau pake state harus pake Class Component: // components/CounterClass.tsx import { Component } from 'react'; type CounterState = { count: number; }; class CounterClass extends Component<{}, CounterState> { constructor(props: {}) { super(props); this.state = { count: 0 }; } handleTambah = () => { this.setState((prev) => ({ count: prev.count + 1 })); }; render() { return ( <div> <p>Jumlah: {this.state.count}</p> <button onClick={this.handleTambah}>Tambah</button> </div> ); } } export default CounterClass; Agak panjang ya 😅 Lifecycle Class Component punya lifecycle methods kayak componentDidMount, componentDidUpdate, dll. componentDidMount() { console.log('Component muncul ke layar!'); } Tapi di Functional Component, kamu bisa ngelakuin hal yang sama pake useEffect: import { useEffect } from 'react'; useEffect(() => { console.log('Komponen dimuat ke layar!'); }, []); Sama fungsinya, tapi jauh lebih simpel dan fleksibel. Konteks “this” Di Class Component, kamu sering harus berurusan sama this, dan itu bisa bikin pusing. this.setState(...); this.props.nama; Sementara di Functional Component, semuanya lebih clean—nggak perlu mikir soal this. Performance? Secara performa, dua-duanya hampir sama di kebanyakan kasus. Tapi karena Functional Component lebih ringan dan mudah dioptimasi (terutama dengan React 18 dan concurrent mode), biasanya jadi pilihan utama buat proyek baru. Singkatnya: Proyek baru? Functional Component + Hooks adalah best choice.Ngurusin proyek lama? Kadang masih ketemu Class Component, dan itu gapapa.Mau belajar dua-duanya? Bagus! Jadi kamu ngerti semua ekosistem React. Contoh Kode Lengkap Functional vs Class Component Studi Kasus: Kita mau bikin komponen UserProfile yang: Nampilin nama & email penggunaBisa toggle status login (on/off) Functional Component (dengan Hooks) // components/UserProfileFunctional.tsx import { Component } from "react"; type UserProfileProps = { name: string; email: string; }; type UserProfileState = { loggedIn: boolean; }; class UserProfileClass extends Component<UserProfileProps, UserProfileState> { constructor(props: UserProfileProps) { super(props); this.state = { loggedIn: false, }; } toggleLogin = () => { this.setState((prev) => ({ loggedIn: !prev.loggedIn, })); }; render() { const { name, email } = this.props; const { loggedIn } = this.state; const statusClass = `rounded-full h-10 w-10 ${ loggedIn ? "bg-green-500" : "bg-red-500" }`; const btnClass = `mt-4 px-4 py-1.5 cursor-pointer rounded-full text-white font-bold ${ loggedIn ? "bg-red-300" : "bg-blue-400" }`; return ( <div className="flex flex-col gap-2 border-2 p-4 rounded-2xl"> <div className="flex gap-2.5 items-center"> <div className={statusClass}></div> <div className="flex flex-col"> <h2 className="font-bold">{name}</h2> <p className="opacity-80 text-[14px]">{email}</p> </div> </div> <button onClick={this.toggleLogin} className={btnClass}> {loggedIn ? "Logout" : "Login"} </button> </div> ); } } export default UserProfileClass; 🟢 Kelebihan: Ringkas dan gampang dibacaGunain useState buat handle loginNggak repot sama this Class Component (dengan state & event) // components/UserProfileClass.tsx import { Component } from "react"; type UserProfileProps = { name: string; email: string; }; type UserProfileState = { loggedIn: boolean; }; class UserProfileClass extends Component<UserProfileProps, UserProfileState> { constructor(props: UserProfileProps) { super(props); this.state = { loggedIn: false, }; } toggleLogin = () => { this.setState((prev) => ({ loggedIn: !prev.loggedIn, })); }; render() { const { name, email } = this.props; const { loggedIn } = this.state; const statusClass = `rounded-full h-10 w-10 ${ loggedIn ? "bg-green-500" : "bg-red-500" }`; const btnClass = `mt-4 px-4 py-1.5 cursor-pointer rounded-full text-white font-bold ${ loggedIn ? "bg-red-300" : "bg-blue-400" }`; return ( <div className="flex flex-col gap-2 border-2 p-4 rounded-2xl"> <div className="flex gap-2.5 items-center"> <div className={statusClass}></div> <div className="flex flex-col"> <h2 className="font-bold">{name}</h2> <p className="opacity-80 text-[14px]">{email}</p> </div> </div> <button onClick={this.toggleLogin} className={btnClass}> {loggedIn ? "Logout" : "Login"} </button> </div> ); } } export default UserProfileClass; 🔴 Kekurangan: Lebih verbosePerlu constructor & this buat handle event & state Jika kalian mendapatkan error seperti berikut, itu karena defaultnya di Next.js semua komponen itu menggunakan server comonent, dan useState itu hanya bisa digunakan di client component. Kalian bisa belajar tentang server component dan client component pada postingan BuildWithAngga tentang Tutorial Next.js 15 Belajar Server dan Client Component Projek Web Booking Hotel. Next.js build error Untuk mengatasi ini, kita tinggal tambahkan "use client" pada baris pertama kode, jadinya seperti ini: "use client"; import { Component } from "react"; type UserProfileProps = { name: string; email: string; }; type UserProfileState = { loggedIn: boolean; }; class UserProfileClass extends Component<UserProfileProps, UserProfileState> { constructor(props: UserProfileProps) { super(props); this.state = { loggedIn: false, }; } toggleLogin = () => { this.setState((prev) => ({ loggedIn: !prev.loggedIn, })); }; render() { const { name, email } = this.props; const { loggedIn } = this.state; const statusClass = `rounded-full h-10 w-10 ${ loggedIn ? "bg-green-500" : "bg-red-500" }`; const btnClass = `mt-4 px-4 py-1.5 cursor-pointer rounded-full text-white font-bold ${ loggedIn ? "bg-red-300" : "bg-blue-400" }`; return ( <div className="flex flex-col gap-2 border-2 p-4 rounded-2xl"> <div className="flex gap-2.5 items-center"> <div className={statusClass}></div> <div className="flex flex-col"> <h2 className="font-bold">{name}</h2> <p className="opacity-80 text-[14px]">{email}</p> </div> </div> <button onClick={this.toggleLogin} className={btnClass}> {loggedIn ? "Logout" : "Login"} </button> </div> ); } } export default UserProfileClass; Tambahkan juga "use client" pada UserProfileClass.tsx. Buka page.tsx lalu ubah jadi seperti ini: import UserProfileClass from "@/components/UserProfileClass"; import UserProfileFunctional from "@/components/UserProfileFunctional"; export default function Home() { return ( <div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]"> <main className="flex flex-col gap-[32px] row-start-2 items-center sm:items-start"> <h3 className="text-2xl font-bold">Functional Component:</h3> <UserProfileFunctional name="Taufan Fatahillah" email="[email protected]" /> <h3 className="text-2xl font-bold">Class Component:</h3> <UserProfileClass name="Jonh Doe" email="[email protected]" /> </main> </div> ); } Hasilnya akan seperti ini: Local Result Catatan Tambahan Untuk proyek baru, disarankan pake Functional Component + HooksTapi belajar Class Component tetep penting kalau kamu ketemu kode lama (legacy code) Kesalahan Umum yang Harus Dihindari Walaupun React keliatannya simpel, tapi banyak juga jebakan yang sering bikin frustrasi. Nih, beberapa kesalahan umum yang sering kejadian: Lupa Typing Props Ini klasik banget. Di TypeScript, semua props harus didefinisiin typenya. Kalau nggak, kamu bakal kehilangan auto-complete dan bisa bikin bug susah dilacak. // ❌ Salah (nggak ada type) function Greet(props) { return <p>Hi, {props.name}</p>; } // ✅ Benar type GreetProps = { name: string; }; function Greet({ name }: GreetProps) { return <p>Hi, {name}</p>; } Gunain any terus-terusan Pakai any itu kayak ngilangin fitur utama TypeScript. Kalau sering pakai any, sama aja kayak balik ke JavaScript biasa 😅 // ❌ Salah const handleClick = (e: any) => { console.log(e.target.value); }; // ✅ Benar const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => { console.log(e.currentTarget.value); }; State di Functional Component Tanpa Tipe React bisa nebak tipenya sih, tapi kalau datanya kompleks, sebaiknya tetap definisiin tipe-nya. // ❌ Salah const [user, setUser] = useState(null); // ✅ Benar type User = { name: string; age: number; }; const [user, setUser] = useState<User | null>(null); Mengakses this di Functional Component Kadang yang baru pindah dari Class suka iseng nulis this.props di Functional 🤦 // ❌ Salah const MyComponent = () => { console.log(this.props); // ini pasti error }; Import React tapi nggak dipakai Sejak React 17, kamu nggak wajib import React di file yang pakai JSX. Tapi kadang orang masih nulis ini padahal gak perlu. // ❌ Nggak perlu kalau nggak pakai React secara eksplisit import React from 'react'; Best Practices dalam Menulis Komponen React + TypeScript Nah, biar kode kamu clean, maintainable, dan scalable, coba terapin beberapa best practice berikut: Pisahin Tipe Props ke File Sendiri (kalau kompleks) Kalau props-nya banyak atau dipakai di banyak komponen, lebih baik dipisah ke file types.ts. // types/User.ts export type User = { name: string; email: string; }; // components/UserCard.tsx import { User } from '@/types/User'; const UserCard = ({ name, email }: User) => { return <div>{name} - {email}</div>; }; Gunain Destructuring Props Lebih rapi dan enak dibaca dibanding props.name, props.email, dst. // ✅ Lebih clean const Profile = ({ name, age }: { name: string; age: number }) => { return <p>{name} - {age}</p>; }; Selalu Type State dengan Jelas Daripada ngandelin inferensi TypeScript, lebih baik kamu tegasin typenya, apalagi kalau datanya nullable atau array. const [todos, setTodos] = useState<TodoItem[]>([]); Gunain React.FC untuk Komponen Simpel Kalau komponen kamu cuma pake props bawaan, React.FC bisa ngebantu banyak (auto children, auto typing props): type AlertProps = { message: string; }; const Alert: React.FC<AlertProps> = ({ message, children }) => { return <div>{message} {children}</div>; }; Break Down Komponen Jadi Kecil-kecil Jangan bikin satu komponen isinya ribuan baris. Pisah jadi komponen kecil yang reusable. // components/Post.tsx const Post = ({ title, author }: { title: string; author: string }) => ( <article> <h2>{title}</h2> <small>by {author}</small> </article> ); Gunakan ESLint + Prettier + TypeScript Strict Mode Biar kode kamu tetap bersih, konsisten, dan minim bug dari awal. ESLint – Linting Code JavaScript/TypeScript ESLint itu semacam "pengawas" kode kita. Dia bantu nyari dan ngasih warning/error kalau ada potensi bug atau style yang nggak konsisten. Contoh: const a = 5 console.log(a) Tanpa ESLint: Kode di atas dianggap sah-sah aja. Dengan ESLint: Bisa dikasih warning karena console.log, titik koma, atau penamaan variabel nggak sesuai aturan. Prettier – Code Formatter Otomatis Prettier fokus ke formatting, bukan logic. Dia bantu rapihin kode secara otomatis. Misalnya: const hello = (name:string)=>{return "Hi "+name} Setelah Prettier: const hello = (name: string) => { return "Hi " + name; }; Auto rapi, konsisten, dan bikin kode enak dibaca tim 😎 TypeScript Strict Mode – Type Safety Ketat strict: true di tsconfig.json artinya kamu bilang ke TypeScript: "Bro, tolong pastiin semua variable, return type, props, dsb harus jelas dan aman ya!” Contoh: // Tanpa strict mode function greet(name) { return 'Hello ' + name; } // Dengan strict mode function greet(name: string): string { return 'Hello ' + name; } Hasilnya: Lebih sedikit bug, lebih pede deploy 🚀 Next.js Include ESLint by Default Kalau kamu bikin proyek Next.js baru (npx create-next-app), Next.js bakal langsung nanya: "Mau aktifin ESLint?" Dan kalau kamu klik "Yes", dia bakal: Auto install ESLintTambah file .eslintrc.jsonKonfig yang cocok buat Next.js Kamu tinggal pakai aja tanpa setup ribet! Bonus: Gabungin Semuanya Untuk workflow terbaik, kamu bisa gabungin keempatnya: ✅ ESLint → cek kode logic & style✨ Prettier → rapihin format otomatis🔐 TypeScript Strict → jaga keamanan dan kejelasan data⚡ Next.js → tinggal pake, udah include tools-nya Perbandingan Singkat Functional vs Class Component (Tabel) AspekFunctional ComponentClass ComponentSintaksFungsi biasa atau arrow functionMenggunakan class dan extends React.ComponentStateGunakan useStateGunakan this.state dan this.setStateLifecycle MethodGunakan useEffectGunakan componentDidMount, componentDidUpdate, dllHooks Support✅ Ya❌ TidakBinding this❌ Tidak perlu✅ PerluKode lebih ringkas✅ Umumnya lebih ringkas❌ Lebih verboseDigunakan di proyek baru✅ Sangat disarankan❌ Jarang, kecuali legacyDukungan TypeScript✅ Sangat baik✅ Tapi butuh lebih banyak kodeTabel perbandingan singkat Kapan Sebaiknya Pakai Functional vs Class? Gunakan Functional Component jika: Kamu baru belajar React (lebih simpel dan modern)Proyek baru menggunakan React 16.8+Ingin menggunakan Hooks seperti useState, useEffect, useContext, dllIngin kode lebih singkat dan mudah dibacaIngin mengikuti best practice terbaru Gunakan Class Component jika: Kamu sedang maintenance proyek lamaProyek menggunakan React versi lama (di bawah 16.8)Komponen sudah sangat besar dan susah untuk diubah total ke function Tapi secara umum, Functional Component adalah standar modern React. Bahkan tim React sendiri merekomendasikan penggunaan fungsi ketimbang class. Penutup Di artikel ini, kita udah bahas lumayan lengkap tentang: Apa itu Functional dan Class Component di ReactCara setup proyek Next.js + TypeScriptContoh kode untuk dua jenis komponen❌ Kesalahan umum yang sering kejadian✅ Best practices biar kode lebih rapi dan scalable📌 Perbandingan dan kapan sebaiknya digunakan 🚀 Rekomendasi Selanjutnya: Tutorial Next JS 15 Belajar Server dan Client Components Projek Web Booking Hotel10 Hal Perlu Dipelajari Pemula Framework Next.js 15Tutorial Next JS Pemula Mengenal Pentingnya WebpackCara Deploy Website Statis ke Vercel lewat GitHub (Lengkap + Contoh)

Kelas Panduan Lengkap Animasi Tailwind CSS v4: Siap Pakai & Custom Sendiri di BuildWithAngga

Panduan Lengkap Animasi Tailwind CSS v4: Siap Pakai & Custom Sendiri

Lo pernah nggak sih ngerasa tampilan website lo itu... flat banget? Kayak hidup segan, mati tak mau. Nah, di era sekarang, tampilan UI yang dinamis dan interaktif tuh udah jadi kebutuhan, bukan sekadar bonus. Salah satu cara paling gampang buat bikin tampilan web lo lebih hidup adalah pakai animasi. Untungnya, kalau lo pakai Tailwind CSS v4, lo nggak perlu repot ngoding animasi dari nol. Tailwind udah nyediain berbagai animasi siap pakai yang tinggal comot. Bahkan, kalau lo ngerasa animasi default-nya kurang nendang, lo bisa bikin custom animasi versi lo sendiri dengan mudah lewat konfigurasi di TailwindCSS. Di artikel ini, gue bakal ajak lo ngulik bareng soal: Animasi bawaan (yang tinggal pakai)Cara bikin animasi custom sendiriTips dan best practice biar animasi lo nggak lebay tapi tetap kece Yuk, kita mulai ngebahas panduan lengkap animasi Tailwind CSS v4, dari yang siap pakai sampai lo bisa bikin animasi versi lo sendiri! Apa Itu Animasi di Tailwind CSS? Sebelum lo langsung terjun pakai animasi bawaan atau bikin yang custom, penting banget buat ngerti dulu konsep dasarnya. Soalnya, animasi di Tailwind itu ada dua jenis utama: transition dan animation. Keduanya punya fungsi beda, tapi sering dipakai barengan buat bikin efek yang mulus. Transition Transition itu lebih ke perubahan gaya secara halus. Misalnya lo mau bikin tombol berubah warna pas di-hover. Nah, transisi ini ngebuat perubahannya nggak langsung “jepret”, tapi pelan-pelan, lebih enak dilihat. Contoh simple: <button class="bg-blue-500 hover:bg-blue-700 transition-colors duration-300"> Klik Gue </button> Lihat kan? Di situ kita pakai transition-colors biar efek hover-nya smooth. Animation Kalau animation, ini lebih kompleks. Lo bisa bikin elemen muter (spin), mantul-mantul (bounce), muncul perlahan, atau bahkan efek masuk dari samping. Tailwind udah sediain beberapa utility animate-* yang bisa langsung lo pake. Contoh: <div class="animate-bounce w-6 h-6 bg-red-500 rounded-full"></div> Hasilnya? Si bulat merah bakal mantul-mantul kayak bola pingpong. Seru, kan? Singkatnya: Transition → buat efek perubahan gaya (warna, ukuran, opacity, dll).Animation → buat efek gerak yang lebih kompleks dan bisa diatur pakai @keyframes. Jadi sekarang lo udah ngerti, kan, perbedaan dasar antara dua jenis animasi di Tailwind CSS? Yuk, lanjut ke bagian yang paling ditunggu: animasi keren siap pakai yang langsung bisa lo comot! Animasi Siap Pakai di Tailwind CSS v4 Tailwind CSS v4 hadir bawa beberapa animasi built-in yang bisa langsung lo pake tanpa harus nulis @keyframes manual. Cukup tambahin class animate-* ke elemen lo, dan boom — elemen lo langsung punya efek gerak yang kece. Animasi Built-in Tailwind CSS v4 + Contoh Penggunaan Lengkap Di bawah ini gue rangkum empat animasi bawaan paling sering dipakai di Tailwind CSS v4, lengkap sama penjelasan “kapan dipakai” dan potongan kode siap copy-paste. Tinggal ganti teks, warna, atau ukuran sesuai selera—langsung hidupin UI lo! 🚀 animate-spin — ikon loading muter Kapan dipakai? Saat butuh indikator proses (loaders, refresher). Cara kerja: elemen diputar 360° terus-menerus. <!-- Loader bulat --> <div class="h-10 w-10 border-4 border-blue-500 border-t-transparent rounded-full animate-spin"></div> <!-- Loader pakai icon (Heroicons/Feather/FontAwesome, dll.) --> <svg class="h-6 w-6 text-blue-500 animate-spin" viewBox="0 0 24 24" fill="none"> <circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" class="opacity-25"/> <path d="M4 12a8 8 0 018-8" stroke="currentColor" stroke-width="4" class="opacity-75"/> </svg> Hasilnya: animate-spin animate-bounce — elemen mantul-mantul Kapan dipakai? Buat narik perhatian (notifikasi, CTA). Cara kerja: elemen lompat naik-turun secara periodik. <!-- Badge notifikasi --> <span class="inline-flex items-center px-3 py-1 text-sm font-medium text-white bg-green-500 rounded-full animate-bounce"> ✔ Berhasil disimpan! </span> <!-- Panah scroll ke bawah --> <svg class="h-8 w-8 text-gray-700 animate-bounce mt-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/> </svg> Hasilnya: animate-bounce Tip: Kebanyakan bounce bikin pusing. Pakai di spot penting aja, lalu matikan setelah user interaksi (via JS → onClick /tambah animate-none). animate-ping — gelombang radar Kapan dipakai? Menandai posisi (online status, titik map). Cara kerja: muncul lingkaran membesar + memudar, terus mengulang. <!-- Titik lokasi dengan efek ping --> <span class="relative flex h-3 w-3"> <span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-red-400 opacity-75"></span> <span class="relative inline-flex rounded-full h-3 w-3 bg-red-500"></span> </span> Hasilnya: animate-ping Tip: Ping agak “berat” di banyak elemen. Batasi di satu-dua titik penting. animate-pulse — shimmer loading Kapan dipakai? Skeleton screen atau button “bernafas”. Cara kerja: opacity elemen naik-turun perlahan. <!-- Skeleton --> <div class="mx-auto w-full max-w-sm rounded-md p-4"> <div class="flex animate-pulse space-x-4"> <div class="size-10 rounded-full bg-gray-200"></div> <div class="flex-1 space-y-6 py-1"> <div class="h-2 rounded bg-gray-200"></div> <div class="space-y-3"> <div class="grid grid-cols-3 gap-4"> <div class="col-span-2 h-2 rounded bg-gray-200"></div> <div class="col-span-1 h-2 rounded bg-gray-200"></div> </div> <div class="h-2 rounded bg-gray-200"></div> </div> </div> </div> </div> Tip: Matikan pulse ketika data udah ready: ganti class ke warna final + hapus animate-pulse. Hasilnya: animate-pulse Bikin Animasi Custom Sendiri di Tailwind CSS v4 Di Tailwind v4 bikin animasi jadi lebih mudah cuma pakai @keyframesaja dijamin gak bakal ribet. Berikut kode untuk konfigurasi Tailwind, masukkan kode ini di tag <head> yaa! Langsung aja kita bikin..! animate-fade-in CSS: <style type="text/tailwindcss"> @theme { --animate-fade-in: fade-in 0.6s ease-out forwards; @keyframes fade-in { 0% { opacity: 0; } 100% { opacity: 1; } } } </style> HTML: <button type="button" class="px-4 py-1.5 bg-blue-500 text-white rounded-md animate-fade-in"> Fade </button> Hasilnya: animate-fade-in animate-slide-in CSS: <style type="text/tailwindcss"> @theme { --animate-slide-in: slide-in 0.5s ease-out forwards; @keyframes slide-in { 0% { transform: translateX(100%); } 100% { transform: translateX(0); } } } </style> HTML: <button type="button" class="px-4 py-1.5 bg-blue-500 text-white rounded-md animate-slide-in"> Slide </button> Hasilnya: animate-slide-in animate-blur CSS: <style type="text/tailwindcss"> @theme { --animate-blur: blur 0.6s ease-out forwards; @keyframes blur { 0% { filter: blur(8px); opacity: 0; } 100% { filter: blur(0); opacity: 1; } } } </style> HTML: <button type="button" class="px-4 py-1.5 bg-blue-500 text-white rounded-md hover:animate-blur" > Blur </button> Hasilnya: animate-blur animate-expand CSS: <style type="text/tailwindcss"> @theme { --animate-expand: expand 0.4s ease-out forwards; @keyframes expand { 0% { transform: scale(0.8); } 100% { transform: scale(1); } } } </style> HTML: <button type="button" class="px-4 py-1.5 bg-blue-500 text-white rounded-md animate-expand"> Expand </button> Hasilnya: animate-expand animate-shrink CSS: <style type="text/tailwindcss"> @theme { --animate-shrink: shrink 0.4s ease-out forwards; @keyframes shrink { 0% { transform: scale(1.1); } 100% { transform: scale(1); } } } </style> HTML: <button type="button" class="px-4 py-1.5 bg-blue-500 text-white rounded-md animate-shrink"> Shrink </button> Hasilnya: animate-shrink animate-pop CSS: <style type="text/tailwindcss"> @theme { --animate-pop: pop 0.3s ease-out; @keyframes pop { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.15); } } } </style> HTML: <button type="button" class="px-4 py-1.5 bg-blue-500 text-white rounded-md animate-pop"> Pop </button> Hasilnya: aniimate-pop animate-shake CSS: <style type="text/tailwindcss"> @theme { --animate-shake: shake 0.6s ease-in-out both; @keyframes shake { 0%, 100% { transform: translateX(0); } 20%, 60% { transform: translateX(-6px); } 40%, 80% { transform: translateX(6px); } } } </style> HTML: <button type="button" class="px-4 py-1.5 bg-blue-500 text-white rounded-md animate-shake"> Shake </button> Hasilnya: animate-shake animate-wobble CSS: <style type="text/tailwindcss"> @theme { --animate-wobble: wobble 1s ease-in-out infinite; @keyframes wobble { 0%, 100% { transform: rotate(0deg); } 25% { transform: rotate(3deg); } 75% { transform: rotate(-3deg); } } } </style> HTML: <button type="button" class="px-4 py-1.5 bg-blue-500 text-white rounded-md animate-wobble"> Wobble </button> Hasilnya: animate-wobble animate-seesaw CSS: <style type="text/tailwindcss"> @theme { --animate-seesaw: seesaw 1.2s ease-in-out infinite; @keyframes seesaw { 0%, 100% { transform: rotate(0deg); } 50% { transform: rotate(8deg); } 60% { transform: rotate(6deg); } 70% { transform: rotate(8deg); } 80% { transform: rotate(0deg); } 90% { transform: rotate(2deg); } } } </style> HTML: <button type="button" class="px-4 py-1.5 bg-blue-500 text-white rounded-md animate-seesaw"> Seesaw </button> Hasilnya: animate-seesaw Rangkuman ✅ Kenapa butuh animasi custom? Karena lo bisa bikin efek sesuai karakter brand lo: masuk dari samping, zoom in, fade out, dll. ✅ Bisa digabung sama Tailwind utility lain? Bisa banget. Lo tetep bisa atur delay, duration, dan ease secara fleksibel. Tips & Best Practices 🚫 Jangan Berlebihan Animasi itu kayak bumbu. Kalau kebanyakan, malah bikin eneg. Pakai secukupnya buat highlight interaksi penting aja (kayak buka modal, hover tombol, dsb). 🧑‍🦯 Gunakan motion-safe Biar animasi nggak mengganggu pengguna yang pakai preferensi "reduce motion", kamu bisa tambahkan prefix ini: <div class="motion-safe:animate-fade">Aman buat semua</div> 📱 Cek Performa di Perangkat Low-End Beberapa animasi berat (seperti shadow blur + scale + opacity sekaligus) bisa bikin patah-patah di HP kentang. Coba tes di device real kalau bisa. Penutup Nah, sampai sini lo udah ngerti gimana powerful-nya animasi di Tailwind CSS v4. Mulai dari animasi siap pakai, bikin custom animasi sendiri pakai CSS, sampai nerapin di komponen kayak modal — semuanya bisa lo lakuin tanpa ribet. Dengan fitur baru di v4, lo bisa: Bikin animasi sekali, pakai di mana aja.Nggak perlu oprek tailwind.config.js.Lebih fleksibel buat trigger animasi via JavaScript. Intinya? UI lo bakal jadi lebih hidup dan interaktif, tanpa harus ngoding panjang-panjang. Kalau lo pengen explore lebih jauh, nih gue kasih beberapa referensi keren: 📘 Dokumentasi Resmi Tailwind CSS - Animation🧪 Tailwind Play - Coba Langsung di Browser Ini gue kasih juga rekomendasi kelas gratis dari BuildWithAngga buat memperdalam TailwindCSS: 1. CSS Tailwind Web Design Kelas ini cocok banget buat lo yang pengen ngedesain website modern dan clean pake Tailwind CSS. Cocok buat pemula maupun yang udah pernah nyoba Tailwind tapi pengen lebih rapi dan estetik. 2. Belajar Tailwind CSS untuk Pemula Website Designer Namanya juga buat pemula, jadi tenang aja, lo bakal dipandu dari nol. Cocok buat lo yang masih suka bingung bedain mt-4 sama mb-4 😆 3. HTML5 + Tailwind CSS: Creating Modern Eye-Catching Website Gabungan antara HTML5 dan Tailwind, lo bakal diajarin bikin website yang gak cuma fungsional tapi juga menarik secara visual. Pokoknya gak cuma ngoding asal tampil. 4. Vue 3 + Tailwind CSS: Membuat Komponen Autocomplete Khusus buat lo yang udah mainan Vue 3, kelas ini ngajarin cara bikin komponen autocomplete yang reusable dan stylish pake Tailwind. Keren buat portofolio lo juga nanti! Semangat eksplor, bro! Animasi itu bukan cuma estetika — tapi juga bagian dari komunikasi desain yang baik. Jangan takut bereksperimen! 💪

Kelas Cara Deploy Website Statis ke Vercel lewat GitHub (Lengkap + Contoh) di BuildWithAngga

Cara Deploy Website Statis ke Vercel lewat GitHub (Lengkap + Contoh)

Daftar Isi PendahuluanProyek Saya dari Shaynakit (FREE)Deploy GitHubPushAturan Online Website Statis di VercelAturan Default (Public)Konvensi Umum di Dunia WebOptimasi Tanpa KonfigurasiMembedakan Source Code & File Siap TayangMencegah Error & Akses File yang Gak PerluLangkah-Langkah PublicAturan Tailwind CSS (Src)Struktur FolderTambahkan file vercel.json di root projectOnline-kan di VercelStep 1: Upload ke GitHubStep 2: Daftar Akun VercelStep 3: Login ke Overview VercelStep 4: Klik “New Project”Step 5: Klik tombol import yang buildwithanggaStep 6: Klik DeployStep 7: HasilPenutup Pendahuluan Punya website statis udah jadi, tampilannya cakep, animasi smooth, warnanya pas... tapi masih ngumpet di folder, Wah, sayang banget dong. Daripada disimpan sendiri dan cuma kamu doang yang lihat, mending langsung naikin ke internet biar bisa dipamerin ke dunia. Caranya? Gampang banget! Cuma butuh dua alat andalan: GitHub buat naro proyeknya, dan Vercel buat nampilin ke internet. Kayak duo maut: satu nyimpen, satu nampilin. Di artikel ini, kita bakal bahas step by step-nya mulai dari upload ke GitHub, sampai proyek kamu tampil kece di Vercel. Nggak perlu ribet, nggak perlu coding aneh-aneh. Yuk, kita gas bareng-bareng. Proyek Saya dari Shaynakit (FREE) Shaynakit - Resto Di tutorial kali ini, kita bakal sru-seruan bareng ngebedah proyek keren dari Shaynakit. Kabar baiknya: ini 100% gratis, dan yang makin mantep, kodenya udah siap tempur. Jadi lo nggak perlu mulai dari nol, tinggal colok, modif dikit, langsung jalan. Nggak ribet, nggak bikin kening berkerut, pokoknya ngoding jadi fun banget! Shaynakit ini tuh kayak buffet buat front-end developer tinggal pilih desain yang lo suka, semua udah sepaket sama kodenya. Ada versi gratis, ada juga yang premium. Tapi santai, yang gratis pun udah cukup loyal kok. Biasanya yang dibatesin cuma jumlah halaman aja. Nah, kalau yang premium? Itu sih udah kayak pesta coding semua elemen tinggal comot, tinggal pakai, beres! Sekarang masuk ke inti dari semuanya: gimana cara dapetin template gratisnya. Gampang banget, nggak perlu mantra khusus. Cukup ikutin langkah-langkah ini: Buka situs utamanya di sini: https://shaynakit.com/landing.Klik menu Register atau langsung aja meluncur ke https://shaynakit.com/register buat daftar akun dulu.Setelah berhasil daftar dan login, buka halaman template yang mau dipakai di sini: https://shaynakit.com/details/resto-restaurant-landing-page-html-tailwind-css-template.Klik tombol Download, lalu pilih opsi Free Trial.Lanjut klik Start Today buat mulai akses gratisnya.Setelah itu, balik lagi ke halaman template tadi dan klik tombol Download sekali lagi.File-nya bakal langsung keunduh dalam format .zip.Simpan file .zip itu ke folder lokal kamu, misalnya ke: ./source-code/resto.zip. Udah, beres. Sekarang kamu punya satu paket desain + kode yang siap kamu eksplor. Tinggal buka, utak-atik, terus bawa ke proyek kamu. Deploy Github Github - My Github GitHub itu ibarat lemari kaca super aman tempat kita naro semua karya codingan kita, entah itu proyek pribadi, kerjaan kelompok, atau bahkan eksperimen tengah malam yang cuma kamu ngerti. Di sanalah para developer dari seluruh dunia numpuk kode mereka, jadi kalau suatu saat butuh, tinggal buka lagi, atau bahkan pamerin ke dunia. Bayangin kamu lagi bikin komik, dan tiap panel kamu simpan rapi satu-satu, lengkap dengan sejarah siapa yang gambar, ubah warna, atau nambah dialog. Nah, GitHub tuh kayak arsip digital yang nyatet semua itu otomatis. Kapan kita perlu? Ya setiap kali kita ngoding dan pengen nyimpen progress, kolaborasi sama temen, atau bahkan ngelamar kerja pakai portfolio. Caranya juga gampang banget, kayak ngirim file ke Google Drive, tapi lebih kece karena setiap versi bisa kamu rewind sesuka hati. Serunya lagi, semua orang bisa intip, bantuin, bahkan ikut kontribusi. Jadi bukan cuma tempat nyimpen, tapi juga tempat ngumpulnya semangat kolaborasi developer sejagat raya. Push Pertama, buka terminal atau Git Bash, dan pastiin kamu udah ada di dalam folder proyek kamu kayak gini misalnya: VSCode - Open Project cd path/ke/buildwithangga selanjutnya kayak ngasih tahu folder kamu: “Eh bro, sekarang kamu udah jadi proyek Git ya” VSCode - Initial Project git init selanjutnya ibarat kamu masukin semua file proyek ke dalam koper buat dibawa ke GitHub. VSCode - Add All Folder git add . selanjutnya commit yaitu kayak ngasih catatan, "Oke ini versi pertama gue ya!" VSCode - Add Comment For Push git commit -m "First commit buildwithangga" Sekarang buka https://github.com ➜ Login ➜ Klik tombol New Repository. Github - New Repository Masukkan nama repo (misal: buildwithangga) Github -Fill in the Input Pilih public/privateJangan centang "Initialize this repository with a README" (biar ga bentrok)Klik Create Repository GitHub bakal kasih kamu URL repo kayak gini: Github -Create Repository <https://github.com/tegarfauzan/buildwithangga.git> Sekarang kita kasih tau Git: “Bro, ini lho alamat rumah barunya di GitHub, simpen di sana ya.” VsCode -Add Remote git remote add origin <https://github.com/tegarfauzan/buildwithangga.git> Waktunya terbang. Kirim proyek kamu ke GitHub, perintahnya gini: git push -u origin master Buka repo GitHub kamu, akhirnya tadaaa! Proyek kamu udah tampil manis di sana, siap dipamerin, dibagikan, atau dideploy ke Vercel. Seperti ini bila sudah berhasil: Github -Result Aturan Online Website Statis di Vercel Vercel - Homepage Bayangin kamu punya laptop yang nyala 24 jam, koneksi internetnya super cepat, dan bisa diakses siapa aja, kapan aja. Nah, Vercel itu ibarat laptop online yang bisa menyimpan dan menjalankan website kamu tanpa ribet. Vercel ini platform hosting yang fokus banget buat front-end dan static site mulai dari HTML biasa, CSS, sampai framework modern kayak Next.js. Kalau kamu punya website statis, artinya website kamu cuma butuh file HTML, CSS, JS, tanpa database atau server dinamis, maka Vercel ini bener-bener sahabat sejati. Misalnya kamu pakai Tailwind CSS via CLI, atau pakai CSS biasa yang ditulis manual. Vercel bakal menyajikan semua file statis itu secepat kilat. Gak perlu server-side processing, cukup upload file-nya dan... BOOM! langsung online. Aturan Default (Public) Kalau kamu deploy atau online-in proyek ke Vercel, biasanya ada cara default yang harus kamu ikutin, yaitu dengan punya folder public. Nah, di bawah ini aku jelasin kenapa sih folder public itu penting banget buat proses deploy kamu di Vercel. 1. Konvensi Umum di Dunia Web Di banyak framework modern kayak Next.js, Vue, atau React, folder public/ itu udah jadi tempat langganan buat nyimpen file-file statis yang langsung bisa diakses browser—misalnya index.html, style.css, gambar .png atau .jpg, file JavaScript, sampai favicon. Nah, karena itu udah jadi kebiasaan umum di dunia web, Vercel juga ngikutin pola itu. Jadi kalau kamu gak bilang apa-apa soal folder mana yang mau ditayangin, Vercel bakal otomatis mikir: “Oh, pasti maksudnya folder public/ nih.” 2. Optimasi Tanpa Konfigurasi Vercel emang didesain biar proses deploy itu semudah klik upload aja. Makanya, mereka bikin folder public/ jadi default. Jadi kalau kamu udah pake public/, kamu gak perlu ribet bikin vercel.json, gak usah ngatur output directory manual, tinggal masukin aja semua file HTML, CSS, JS kamu ke dalam public/, terus deploy, langsung tayang! Ini sejalan sama filosofi mereka yang namanya zero-config, alias: “Kalau kamu gak ngatur apa-apa, tenang aja, kami udah siapin jalur yang paling aman dan paling umum buat kamu.” 3. Membedakan Source Code & File Siap Tayang Misalnya kamu punya folder: src/ → tempat source code, Tailwind, JSX, dll public/ → hasil akhir / file yang siap tayang Nah, Vercel tuh paham banget alurnya developer. Mereka tahu kalau folder src/ itu biasanya isinya masih bahan mentah, perlu di-build dulu, belum siap tayang. Sedangkan folder public/ itu udah kayak etalase, tempat semua file final yang siap disajikan ke pengunjung. Jadi, berdasarkan asumsi itu, Vercel secara default langsung ngarahin domain kamu ke isi dari public/, tanpa kamu perlu ngatur-ngatur dulu. 4. Mencegah Error & Akses File yang Gak Perlu Dengan cuma nyajiin file yang ada di dalam folder public/, Vercel juga sekalian ngasih perlindungan ekstra. Jadi, file-file internal kayak .env, package.json, atau file config lainnya gak bakal ikut-ikutan tampil ke publik. Ini juga bikin performa website kamu lebih oke, karena yang disajikan cuma file statis yang memang dibutuhkan. Plus, kamu terhindar dari error aneh atau kebocoran file development yang sebenarnya gak perlu dilihat sama pengunjung. Aman, ringan, dan rapi. 5. Langkah-Langkah Public Bayangin kamu bikin website company profile semacam halaman resmi buat bisnis kamu. Atau mungkin kamu lagi bikin website portofolio buat nunjukin karya-karya kamu, landing page promosi produk, blog pribadi yang tampilannya simpel, atau sekadar playground buat ngoprek HTML & CSS Semua itu masuk ke dalam kategori website statis, karena isinya cuma HTML, CSS, dan JavaScript, tanpa backend atau database yang ribet. Kalau kayak gitu kamu perlu ikutin di bawah ini agar bisa online di Vercel: Folder public/ : Taruh semua file kamu di sini: index.html, style.css, main.js, gambar, dll. project/ └── public/ ├── index.html ├── style.css └── main.js Tipsnya simpel: pastikan kamu punya file index.html di dalam folder public/ dan semua path ke CSS atau JS menggunakan awalan / supaya pakai absolute path (contohnya /style.css bukan style.css), dan enaknya lagi, kamu gak perlu bikin file vercel.json kalau pakai cara ini, jadi lebih simpel, bersih, dan langsung bisa deploy tanpa ribet. Aturan Tailwind CSS (Src) Biasanya nih, kalau kamu pakai Tailwind CSS lewat Tailwind CLI, kamu bakal sering nemuin yang namanya folder src. Contohnya, kamu jalanin perintah kayak npx @tailwindcss/cli -i ./src/input.css -o ./src/output.css --watch, itu artinya Tailwind lagi nge-proses file yang ada di dalam folder src. Nah, cara pakai folder src ini nggak cuma buat Tailwind doang, kamu juga bisa terapin buat folder lain kalau mau. Aku bahas Tailwind karena secara default CLI-nya memang ngarah ke folder src, jadi supaya bisa lancar online di Vercel, kamu perlu trik khusus biar hasilnya bisa langsung dipakai tanpa ribet. Langsung aja pahami berikut : 1. Struktur Folder project/ ├── vercel.json ├── package.json ├── package.lock.json ├── .gitignore └── src/ ├── index.html ├── input.css ├── output.css └── script.js Misalkan kamu punya struktur proyek Tailwind kayak gini. Biasanya, di Tailwind versi 4, kamu nggak selalu butuh file tailwind.config.js secara default karena Tailwind udah makin pintar nangkep settingan dasar tanpa harus dikonfigurin manual. Nah, keuntungan dari versi 4 ini, kamu bisa dapetin struktur Tailwind yang lebih simpel dan bersih kayak yang kamu lihat sekarang, jadi nggak ribet harus bikin banyak file konfigurasi dulu sebelum mulai ngoding. 2. Tambahkan file vercel.json di root project { "builds": [ { "src": "src/**/*", "use": "@vercel/static" } ], "routes": [ { "src": "/(.*)", "dest": "/src/$1" } ] } Builds { "src": "src/**/*", "use": "@vercel/static" } Artinya semua file yang ada di dalam folder src/ beserta subfoldernya akan dianggap sebagai file statis oleh Vercel, karena plugin @vercel/static dipakai untuk langsung menyajikan file HTML, CSS, dan JS tanpa proses build, dan tanda */* menunjukkan semua file dan folder dalam src/ akan diproses dan disajikan langsung sebagai file statis oleh Vercel. Routes { "src": "/(.*)", "dest": "/src/$1" } Artinya, setiap permintaan ke URL apa pun (karena /(.*) itu wildcard) akan diarahkan ke file yang sama persis di dalam folder src/. Misalnya, kalau kamu akses /about.html, Vercel bakal sajikan file /src/about.html, atau kalau kamu akses /assets/input.css, dia akan ambil file /src/assets/input.css. Jadi, walaupun filenya ada di dalam folder src/, URL yang terlihat di browser tetap kelihatan dari root, bikin semuanya jadi rapi dan gampang diakses. Online-kan di Vercel Cara ng-online-in proyek statis ke Vercel dari folder public atau src, itu sebenarnya gampang banget, apalagi kalau konfigurasi yang tadi udah kamu terapin, ibaratnya kamu udah nyiapin "bekal" buat piknik, tinggal berangkat doang nih. Nah, sekarang, kalau bekalnya udah siap (alias konfigurasi beres), langkah selanjutnya tinggal eksekusi. Eksekusinya kayak gini: Step 1: Upload ke GitHub Github -Repository Project Pastiin dulu proyek kamu udah nangkring di GitHub. Anggep aja GitHub itu semacam cloud storage khusus developer, tempat nyimpen proyek biar bisa dijemput sama Vercel nanti. Kalau belum, upload dulu yaa, pakai Git, GitHub Desktop, atau upload manual langsung di GitHub. Step 2: Daftar Akun Vercel Sekarang buka https://vercel.com dan bikin akun Vercel. Ini kayak kamu harus punya akun ojek online dulu sebelum bisa mesen makanan. Step 3: Login ke Overview Vercel Vercel -Overview Udah punya akun? Mantap! Sekarang login ke Vercel, dan kamu bakal lihat dashboard-nya. Step 4: Klik “New Project” Vercel - Add New Langsung aja klik tombol “New Project”. Vercel akan minta akses ke akun GitHub kamu, karena dia pengin tau proyek mana yang mau kamu online-in. Step 5: Klik tombol import yang buildwithangga Vercel - Import Project Step 6: Klik Deploy Vercel - Klik Deploy Langsung klik Deploy, dan... Bumm! Proyek kamu langsung ngacir ke internet. Ibarat masak mie instan, air udah mendidih, bumbu udah masuk, mie udah mateng, tinggal sajikan. Step 7: Hasil Vercel - Deploy Success Kalau kayak gini artinya sudah berhasil. Kamu tinggal klik tombol continue aja nanti bakal di arahin untuk akses websitenya. Penutup Nah, itu dia langkah-langkah lengkap buat ngedeploy website statis ke Vercel lewat GitHub. Gampang banget, kan? Kayak lagi ngantar paket, asal alamatnya bener (GitHub), kurirnya siap (Vercel), tinggal tunggu aja nyampe ke internet. Sekarang website kamu udah bisa diakses siapa aja, kapan aja, dari mana aja. Tinggal kamu kembangkan lebih keren lagi, atau bahkan... kasih sentuhan backend dikit kalau udah siap naik level. Semoga artikel ini ngebantu kamu yang pengen tampil profesional dengan cara yang simpel. Sampai ketemu di tutorial selanjutnya, dan selamat berselancar di dunia web.

Kelas Contoh Web HTML dan Tailwind CSS yang Sudah Jadi (Siap Pakai) di BuildWithAngga

Contoh Web HTML dan Tailwind CSS yang Sudah Jadi (Siap Pakai)

Daftar Isi PendahuluanPreviewDownloadCara MenggunakanStruktur Folder & FileKode HTML & Tailwind CSSHeadBodyHeaderMainAds SectionWhy us?Popular restaurantNear you restaurantExpert chefKode JavaScriptcard.jsdropdown.jsmain.jstab.jsinput.css.gitignorepackage.jsonvercel.jsonCara MengeditBeragam Template HTML + Tailwind CSS (FREE) 🙌Penutup Pendahuluan Pernah nggak sih kamu pengen bikin website tapi bingung harus mulai dari mana? Rasanya kayak mau masak, tapi nggak punya resep bahan-bahan yang udah ada, dan nggak tahu urutannya gimana. Nah, artikel ini hadir buat bantu kamu yang baru belajar HTML dan Tailwind CSS biar nggak nyasar di awal. Di sini, kamu bakal nemuin contoh web sederhana yang udah jadi, lengkap sama struktur HTML-nya dan gaya-gaya dasarnya pakai Tailwind CSS. Serunya lagi, ini bukan cuma contoh doang, kamu bisa langsung pakai atau modifikasi sesuai gaya kamu. Jadi, daripada pusing bikin dari nol, mending mulai dari sesuatu yang udah siap santap. Siap? Yuk, kita bahas bareng-bareng. Preview Yuk, intip dulu tampilannya! Gambar di bawah ini semacam 'cuplikan trailer' dari proyek keren yang bakal kamu pelajari. Resto - Open in Browser Kalau kamu penasaran dan pengin lihat langsung hasil jadinya, tenang aja aku udah siapin link-nya di bawah ini ya. Tinggal klik aja. https://resto-rose.vercel.app/ Download Kamu nggak cuma bisa lihat aja, lho! Di sini aku juga udah sediain file proyeknya biar kamu bisa download dan cobain sendiri. Tinggal klik link Google Drive di bawah ini, terus buka pakai text editor favorit kamu. Aku sendiri sih pakai Visual Studio Code. Yuk klik di bawah ini: File Zip Proyek Resto Cara Menggunakan Biar proyeknya bisa kamu utak-atik, pastikan dulu kamu udah buka foldernya di text editor, ya. Di sini aku pakai Visual Studio Code. Terus, buka terminalnya dengan cara pencet Ctrl + backtick (``` yang di bawah tombol Esc itu, lho). Kalau udah, tinggal jalanin perintah di bawah ini: npm install Kalau kamu udah buka terminal dan ketik npm install, itu akan menjalankan proses buat ngedownload semua 'alat bantu' yang dibutuhin proyek ini. Ibaratnya kayak kamu baru pindah rumah, terus npm install itu tugas kamu buat masukin semua perabotan yang udah ada di daftar biar rumahnya langsung siap ditempati. Jadi, semua package yang dibutuhin bakal otomatis didownload ke folder node_modules. Kamu tinggal duduk manis nunggu prosesnya selesai. Setelah semua ‘perabotan’ alias package-nya udah masuk berkat npm install, sekarang saatnya kita ‘nyalain’ rumahnya biar bisa dipakai. Jalankan perintah di bawah ini buat mulai proyeknya. Anggap aja ini kayak kamu nyalain lampu dan buka pintu langsung keliatan deh isi websitenya di browser. Yuk jalankan perintah ini: npm run dev Struktur Folder & File Sekarang kita intip dulu isi ‘dalaman’ proyek ini, yuk! Jadi, di dalam folder utama bernama RESTO-MAIN, kamu bakal nemuin beberapa bagian penting kayak ini: /RESTO-MAIN ├── node_modules/ ├── src/ │ ├── assets/ │ │ └── images/ │ │ ├── icons/ │ │ │ ├── arrow_bottom.svg │ │ │ ├── arrow_right.svg │ │ │ ├── cost.svg │ │ │ ├── discount.svg │ │ │ ├── eat.svg │ │ │ ├── info_square.svg │ │ │ ├── Location.svg │ │ │ ├── logo.svg │ │ │ ├── search.svg │ │ │ ├── shield.svg │ │ │ ├── sign_right.svg │ │ │ ├── sparkle.svg │ │ │ └── star.svg │ │ └── thumbnails/ │ │ ├── ads-1.png │ │ ├── ads-2.png │ │ ├── chef-1.png │ │ ├── chef-2.webp │ │ ├── chef-3.webp │ │ ├── chef-4.webp │ │ ├── near-1.png │ │ ├── near-2.png │ │ ├── near-3.png │ │ ├── resto-1.png │ │ ├── resto-2.png │ │ ├── resto-3.png │ │ └── resto-4.png │ ├── js/ │ │ ├── card.js │ │ ├── dropdown.js │ │ ├── main.js │ │ └── tab.js │ ├── index.html │ ├── input.css │ └── output.css ├── .gitignore ├── package-lock.json ├── package.json └── vercel.json VSCode - Folder and File Structure Sidebar node_modules/ Ini semacam gudang besar berisi semua alat bantu (dependencies) yang tadi kamu download lewat npm install. Jangan diutak-atik manual ya, cukup diurus sama npm-nya aja. src/ Ini jantungnya proyek kita semua file inti buat tampilannya ada di sini. assets/ Tempat semua media kayak gambar dan ikon disimpan. images/ Nah, di sinilah seluruh gambar dikelompokkan jadi dua bagian: icons/ Berisi semua file SVG yang dipakai buat ikon-ikon kecil, kayak arrow_right.svg, search.svg, sampai logo.svg. Ibaratnya, ini koleksi stiker digital kamu buat dekor halaman. thumbnails/ Ini kumpulan gambar tampilan kayak iklan, chef, resto, dan lokasi. Bayangin ini kayak album foto buat mempercantik tampilan website. js/ Folder ini isinya semua file JavaScript yang ngatur interaksi di website kamu. Mulai dari card.js buat fitur kartu, dropdown.js buat menu turun, sampai tab.js buat navigasi tab. index.html File utama HTML yang jadi fondasi tampilan web kamu. input.css dan output.css input.css tempat kamu nulis style Tailwind, dan output.css adalah hasil akhirnya setelah diproses sama Tailwind. Anggap aja ini kayak nulis resep di input.css, dan ngeluarin masakan siap saji di output.css. .gitignore File ini bilang ke Git: "Hei, jangan ikutin file-file tertentu ya, kayak node_modules." package.json dan package-lock.json Ini dua file penting buat npm. package.json nyatet semua paket yang kamu butuhin dan script yang bisa dijalankan, sedangkan package-lock.json ngunci versi-versinya biar nggak berubah-ubah. vercel.json File ini buat ngasih tau Vercel (platform hosting) gimana cara ngejalanin dan build proyek kamu. Kode HTML & Tailwind CSS Head <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Resto - Landing Page</title> <link href="./output.css" rel="stylesheet" /> <!-- Swiper CSS --> <link rel="stylesheet" href="<https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css>" /> </head> Bayangin aja bagian <head> itu kayak ruang kontrolnya halaman web kamu, tempat kamu ngatur semuanya biar berjalan mulus. Di sini, kamu kasih tahu browser, “Hey, pakai karakter UTF-8 ya biar tulisan nggak aneh-aneh!” terus juga atur supaya tampilan web kamu bisa adaptasi dengan baik di semua gadget, dari HP sampai layar gede, lewat viewport. Nah, kamu juga kasih judul keren buat halaman kamu, misalnya “Resto - Landing Page,” biar pengunjung langsung tahu mereka di mana. Gak cuma itu, kamu sambungin file output.css supaya semua gaya kece dari Tailwind bisa nempel di halaman kamu. Terus ada juga link ke Swiper JS dari CDN, ini nih yang bikin slider kamu jadi asik dan interaktif. Jadi intinya, di ruang kontrol ini kamu siapin semua senjata dulu sebelum tampilan aslinya keluar ke layar. Body <body> <!-- Navbar --> <header class="px-[60px] mt-[60px]"> ... </header> <main> ... </main> <!-- Swiper JS --> <script src="<https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js>"></script> <script src="./js/main.js"></script> <script src="./js/tab.js"></script> <script src="./js/card.js"></script> <script src="./js/dropdown.js"></script> </body> Disini <main> ini spot utama buat semua konten keren kamu, entah itu gambar, teks, atau fitur-fitur yang pengen kamu pamerin. Pokoknya, semua yang pengunjung cari pasti ada di sini. Nah, jangan lupa, biar web kamu hidup dan gak cuma statis, kamu juga masukin beberapa file JavaScript di bawah sini. Ada Swiper JS dari CDN, yang tadi udah kita bahas buat bikin slider jadi keren dan interaktif. Terus, ada beberapa skrip lokal kayak main.js, tab.js, card.js, dan dropdown.js. Mereka ini kayak kru di balik layar yang jaga biar fitur-fitur di web kamu jalan mulus, mulai dari navigasi tab, interaksi kartu, sampai dropdown yang muncul pas kamu klik sesuatu. Jadi, semua bagian ini kerja barengan biar pengalaman kamu dan pengunjung makin asik dan dinamis! Header Resto - Navtop Section <header class="px-[60px] mt-[60px]"> <div class="flex justify-between items-center min-w-[1320px] h-14"> <nav> <ul class="flex gap-[30px]"> <!-- Home --> <li class="group flex flex-col justify-between h-[28px]"> <a href="#" class="text-[16px] leading-[24px] font-medium">Home</a> <span class="transition-all duration-300 h-0.5 w-full rounded-full bg-secondary opacity-0 group-hover:opacity-100"></span> </li> <!-- Category Dropdown --> <li class="dropdown relative flex flex-col cursor-pointer"> <div class="flex items-center h-6 w-full"> <button type="button" class="dropdown-button text-[16px] leading-[24px] font-medium cursor-pointer w-full text-left">Category</button> <div class="flex items-center justify-center h-6 w-6 shrink-0"> <img src="./assets/images/icons/arrow_bottom.svg" alt="" class="dropdown-arrow transition-transform duration-300" /> </div> </div> <ul class="dropdown-menu z-50 opacity-0 flex transition-all duration-300 absolute top-full left-0 mt-2 min-w-[150px] flex-col gap-1.5 bg-white rounded-xl py-2.5 shadow-lg"> <li class="group"> <a href="#" class="block py-1.5 px-5 text-[16px] leading-[24px] font-medium transition-all duration-300 hover:bg-secondary/5"> Foods </a> </li> <li class="group"> <a href="#" class="block py-1.5 px-5 text-[16px] leading-[24px] font-medium transition-all duration-300 hover:bg-secondary/5"> Drinks </a> </li> <li class="group"> <a href="#" class="block py-1.5 px-5 text-[16px] leading-[24px] font-medium transition-all duration-300 hover:bg-secondary/5"> Desserts </a> </li> </ul> </li> <!-- Services --> <li class="group flex flex-col justify-between h-[28px]"> <a href="#" class="text-[16px] leading-[24px] font-medium">Services</a> <span class="transition-all duration-300 h-0.5 w-full rounded-full bg-secondary opacity-0 group-hover:opacity-100"></span> </li> <!-- About Us --> <li class="group flex flex-col justify-between h-[28px]"> <a href="#" class="text-[16px] leading-[24px] font-medium">About us</a> <span class="transition-all duration-300 h-0.5 w-full rounded-full bg-secondary opacity-0 group-hover:opacity-100"></span> </li> </ul> </nav> <!-- Logo --> <a href="./"> <img src="./assets/images/icons/logo.svg" alt="" class="h-[38px] w-auto" /> </a> <!-- Search and Login --> <div class="flex gap-5"> <form class="group flex w-[322px] gap-2.5 py-4 px-7 bg-white rounded-[12px] transition-all duration-300 focus-within:ring-2 focus-within:ring-secondary hover:ring-2 hover:ring-secondary"> <div class="flex items-center justify-center h-6 w-6 shrink-0"> <img src="./assets/images/icons/search.svg" alt="" class="" /> </div> <input type="text" class="w-full text-[16px] font-medium leading-6 placeholder-placeholder group-hover:outline-none focus:outline-none" placeholder="Search your favorite food" /> </form> <a href="#" class="w-[104px] h-14 flex items-center justify-center bg-secondary/10 text-secondary text-[16px] font-semibold leading-6 rounded-[12px] transition-all duration-300 hover:shadow-secondary"> Log In </a> </div> </div> </header> Jadi header di sini ibarat pintu gerbang keren yang nyambut pengunjung waktu mereka buka web kamu. Dibungkus dengan padding kiri-kanan 60px dan jarak atas 60px biar nggak mepet, tampilannya jadi lega dan enak dipandang. Di dalamnya ada container utama yang pakai flex buat ngejalanin semua item secara horizontal dan rapi: navigasi, logo, sama area search + login. Navigasi (nav): Ini kayak peta jalan buat pengunjung. Ada beberapa menu utama yang kamu kasih gaya kece supaya interaktif: Home, Services, About Us: tiap menu ini ada underline halus yang muncul pas kamu hover, kayak lampu yang nyala, biar pengunjung tau lagi hover di mana.Category Dropdown: Ini bagian spesial. Kamu bikin dropdown yang pas di-hover atau diklik, muncul menu pilihan makanan, minuman, sama dessert. Tombolnya ada icon panah kecil yang animasi halus muter saat dropdown dibuka, biar keliatan hidup dan jelas. Logo: Tengah-tengah ada logo kamu yang keren, ukurannya pas biar nggak terlalu gede tapi tetap eye-catching. Jadi kayak tanda pengenal yang selalu ada di tengah panggung. Search dan Login: Di kanan, kamu punya form search yang tampilannya stylish, lengkap dengan ikon kaca pembesar. Saat kamu klik di kolom search, muncul efek ring warna secondary yang bikin terasa interaktif dan profesional. Di sebelahnya ada tombol “Log In” yang simple tapi punya hover efek dengan bayangan halus jadi ngajak banget pengunjung buat klik dan masuk. Main <main> <!-- Ads Section --> <section class="relative flex justify-center"> ... </section> <!-- Why us? --> <section class="flex justify-center"> ... </section> <!-- Popular restaurant --> <section class="flex justify-center"> ... </section> <!-- Near you restaurant --> <section class="flex justify-center"> ... </section> <!-- Expert chef --> <section class="mt-[60px] mb-[123px] flex justify-center"> ... </section> </main> Bayangin <main> ini kayak etalase utama di toko kamu, tempat semua hal menarik dan andalan ditampilkan buat pengunjung. Di sini, kamu punya beberapa section yang berjajar rapi, masing-masing punya perannya sendiri: Ads Section: Ini semacam billboard digital kamu, tempat kamu pasang iklan keren atau promo spesial biar pengunjung langsung tergoda dan ngelirik.Why Us?: Nah, ini kayak momen kamu ngejelasin kenapa pengunjung harus pilih tempat kamu. Bisa berisi alasan-alasan kece yang bikin kamu beda dan layak dipilih.Popular Restaurant: Di sini kamu pamerin restoran-restoran hits yang lagi naik daun. Jadi pengunjung bisa langsung lihat mana yang lagi favorit orang banyak.Near You Restaurant: Sekarang makin personal! Bagian ini ngasih rekomendasi restoran yang deket sama lokasi pengunjung, biar mereka gampang cari makan tanpa jauh-jauh.Expert Chef: Ini bagian yang kasih sentuhan spesial, ngenalin chef-chef jagoan yang bikin makanan di restoran kamu makin spesial. Dengan jarak atas dan bawah yang pas, tampilannya jadi bernafas dan elegan. Ads Section Resto - Ads Section <section class="relative flex justify-center"> <div class="swiper !pl-[140px] !pr-[60px]"> <div class="swiper-wrapper flex py-[60px]"> <!-- Item 1 --> <div class="group swiper-slide relative !w-[1128px] !h-[564px]"> <div class="w-full h-full rounded-3xl overflow-clip"> <img src="./assets/images/thumbnails/ads-1.png" alt="" class="object-cover w-full" /> </div> <div class="absolute transition-all duration-300 opacity-0 group-hover:opacity-100 flex flex-col gap-1 -left-20 top-[133px] bottom-[134px] h-[297px] w-[390px] rounded-3xl bg-white pl-6 py-6 pr-[140px]"> <div class="flex items-center h-[27px] w-[210px]"> <div class="flex gap-[6.5px] -mt-[3px] pr-[3px]"> <img src="./assets/images/icons/star.svg" alt="" class="w-[18px] shrink-0" /> <img src="./assets/images/icons/star.svg" alt="" class="w-[18px] shrink-0" /> <img src="./assets/images/icons/star.svg" alt="" class="w-[18px] shrink-0" /> <img src="./assets/images/icons/star.svg" alt="" class="w-[18px] shrink-0" /> <img src="./assets/images/icons/star.svg" alt="" class="w-[18px] shrink-0" /> </div> <div class="flex gap-1 items-center"> <p class="text-lg leading-[27px] font-semibold text-star">5.0</p> <p class="text-[16px] text-muted">(5.2K+)</p> </div> </div> <h2 class="text-[22px] font-semibold leading-[33px]">Padang Restaurant</h2> <p class="text-lg font-semibold leading-[27px] text-muted">IDR 49.999 - IDR 560.000</p> <div class="flex gap-1.5 items-center"> <div class="h-6 w-6 flex items-center justify-center"> <img src="./assets/images/icons/Location.svg" alt="" class="h-6 w-6" /> </div> <p class="text-[16px] leading-6 text-muted">Padang, Indonesia</p> </div> <div class="mt-[30px] flex flex-col gap-3"> <a href="#" class="flex items-center justify-center text-[16px] font-semibold leading-6 bg-primary rounded-[12px] w-[226px] h-14 hover:shadow-primary transition-all duration-300"> <span>Make Reservation</span> <img src="./assets/images/icons/arrow_right.svg" alt="" class="" /> </a> <div class="flex gap-1.5 items-center"> <img src="./assets/images/icons/info_square.svg" alt="" class="h-6 w-6" /> <p class="text-[16px] leading-6 text-muted">No extra cost</p> </div> </div> </div> </div> <!-- Item 2 --> <div class="group swiper-slide relative !w-[1128px] !h-[564px]"> <!-- ini image --> <div class="w-full h-full rounded-3xl overflow-clip"> <img src="./assets/images/thumbnails/ads-2.png" alt="" class="object-cover w-full" /> </div> <!-- ini details --> <div class="absolute transition-all duration-300 opacity-0 group-hover:opacity-100 flex flex-col gap-1 -left-20 top-[133px] bottom-[134px] h-[297px] w-[390px] rounded-3xl bg-white pl-6 py-6 pr-[140px]"> <div class="flex items-center h-[27px] w-[210px]"> <div class="flex gap-[6.5px] -mt-[3px] pr-[3px]"> <img src="./assets/images/icons/star.svg" alt="" class="w-[18px] shrink-0" /> <img src="./assets/images/icons/star.svg" alt="" class="w-[18px] shrink-0" /> <img src="./assets/images/icons/star.svg" alt="" class="w-[18px] shrink-0" /> <img src="./assets/images/icons/star.svg" alt="" class="w-[18px] shrink-0" /> <img src="./assets/images/icons/star.svg" alt="" class="w-[18px] shrink-0" /> </div> <div class="flex gap-1 items-center"> <p class="text-lg leading-[27px] font-semibold text-star">5.0</p> <p class="text-[16px] text-muted">(5.2K+)</p> </div> </div> <h2 class="text-[22px] font-semibold leading-[33px]">Jember Restaurant</h2> <p class="text-lg font-semibold leading-[27px] text-muted">IDR 39.999 - IDR 460.000</p> <div class="flex gap-1.5 items-center"> <div class="h-6 w-6 flex items-center justify-center"> <img src="./assets/images/icons/Location.svg" alt="" class="h-6 w-6" /> </div> <p class="text-[16px] leading-6 text-muted">Jember, Indonesia</p> </div> <div class="mt-[30px] flex flex-col gap-3"> <a href="#" class="flex items-center justify-center text-[16px] font-semibold leading-6 bg-primary rounded-[12px] w-[226px] h-14 hover:shadow-primary transition-all duration-300"> <span>Make Reservation</span> <img src="./assets/images/icons/arrow_right.svg" alt="" class="" /> </a> <div class="flex gap-1.5 items-center"> <img src="./assets/images/icons/info_square.svg" alt="" class="h-6 w-6" /> <p class="text-[16px] leading-6 text-muted">No extra cost</p> </div> </div> </div> </div> </div> </div> </section> Bagian HTML ini tuh kayak etalase digital yang keren banget buat nunjukin restoran-restoran kece pakai Swiper.js. Bayangin kamu lagi liat-liat brosur restoran, tapi versi modern, tinggal geser aja. Setiap slide itu semacam kartu gede berisi gambar restoran yang menggoda. Nah, pas kamu arahkan kursor ke gambarnya, langsung muncul detail kayak rating bintang lima, nama tempat, harga, sampai tombol buat langsung reservasi. Semuanya muncul mulus kayak sulap gara-gara efek opacity yang berubah waktu kamu hover. Slide-nya sendiri ukurannya udah diset fix biar tampilannya konsisten dan mantap, jadi kamu nggak perlu khawatir layout-nya acak-acakan. Gambar restorannya dibikin full tampil biar kesan "wah" langsung dapet. Terus bagian infonya muncul di sebelah kiri gambar, kayak pop-up brosur kecil yang nyodorin promo pas kamu lagi lewat. Kesan interaktif dan mewahnya dapet banget deh, cocok kalau kamu mau bikin orang langsung pengen booking tempat dari tampilan pertama. Why us? Resto - Why us? section <section class="flex justify-center"> <div class="w-[1320px] m-[60px]"> <div class="flex w-fit mx-auto flex-col gap-1.5 text-center mb-[41px]"> <span class="text-[16px] leading-6 font-semibold text-secondary uppercase">Special Benefit For You</span> <h2 class="text-[28px] leading-[42px] font-semibold">Why Should Choose Us?</h2> </div> <div class="flex w-full gap-5"> <!-- Item 1 --> <div class="flex flex-col gap-[54px] h-fit pt-6 min-h-[347px] w-[315px] bg-white rounded-3xl"> <div class="flex flex-col gap-6"> <div class="ml-[117px] mr-[118px] flex items-center justify-center h-20 w-20 rounded-full bg-primary/15"> <img src="./assets/images/icons/discount.svg" alt="" class="h-[58px] w-[58px]" /> </div> <h3 class="text-lg leading-[27px] font-semibold text-center">Extra Discounts</h3> <p class="w-[267px] -mt-3 mx-6 text-[16px] leading-6 text-muted text-center">Get your special discount by using our reservation</p> </div> <a href="#" class="w-full mt-auto h-[72px] flex items-center justify-center text-[16px] leading-6 font-semibold rounded-t-xl rounded-b-3xl transition-all duration-300 hover:bg-primary"> <span>View Details</span> <img src="./assets/images/icons/arrow_right.svg" alt="" class="h-6 w-6" /> </a> </div> <!-- Item 2 --> <div class="flex flex-col gap-[54px] h-fit pt-6 min-h-[347px] w-[315px] bg-white rounded-3xl"> <div class="flex flex-col gap-6"> <div class="ml-[117px] mr-[118px] flex items-center justify-center h-20 w-20 rounded-full bg-primary/15"> <img src="./assets/images/icons/eat.svg" alt="" class="h-[58px] w-[58px]" /> </div> <h3 class="text-lg leading-[27px] font-semibold text-center">Come and Eat</h3> <p class="w-[267px] -mt-3 mx-6 text-[16px] leading-6 text-muted text-center">Get your own table quickly & without waiting in line</p> </div> <a href="#" class="w-full mt-auto h-[72px] flex items-center justify-center text-[16px] leading-6 font-semibold rounded-t-xl rounded-b-3xl transition-all duration-300 hover:bg-primary"> <span>View Details</span> <img src="./assets/images/icons/arrow_right.svg" alt="" class="h-6 w-6" /> </a> </div> <!-- Item 3 --> <div class="flex flex-col gap-[54px] h-fit pt-6 min-h-[347px] w-[315px] bg-white rounded-3xl"> <div class="flex flex-col gap-6"> <div class="ml-[117px] mr-[118px] flex items-center justify-center h-20 w-20 rounded-full bg-primary/15"> <img src="./assets/images/icons/cost.svg" alt="" class="h-[58px] w-[58px]" /> </div> <h3 class="text-lg leading-[27px] font-semibold text-center">No Extra Fee</h3> <p class="w-[267px] -mt-3 mx-6 text-[16px] leading-6 text-muted text-center">Get tax free if you want to order food and make a reservation</p> </div> <a href="#" class="w-full mt-auto h-[72px] flex items-center justify-center text-[16px] leading-6 font-semibold rounded-t-xl rounded-b-3xl transition-all duration-300 hover:bg-primary"> <span>View Details</span> <img src="./assets/images/icons/arrow_right.svg" alt="" class="h-6 w-6" /> </a> </div> <!-- Item 4 --> <div class="flex flex-col gap-[54px] h-fit pt-6 min-h-[347px] w-[315px] bg-white rounded-3xl"> <div class="flex flex-col gap-6"> <div class="ml-[117px] mr-[118px] flex items-center justify-center h-20 w-20 rounded-full bg-primary/15"> <img src="./assets/images/icons/sparkle.svg" alt="" class="h-[58px] w-[58px]" /> </div> <h3 class="text-lg leading-[27px] font-semibold text-center">Guaranteed Cleanliness</h3> <p class="w-[267px] -mt-3 mx-6 text-[16px] leading-6 text-muted text-center">We ensure the cleanliness of the place as well as the food</p> </div> <a href="#" class="w-full mt-auto h-[72px] flex items-center justify-center text-[16px] leading-6 font-semibold rounded-t-xl rounded-b-3xl transition-all duration-300 hover:bg-primary"> <span>View Details</span> <img src="./assets/images/icons/arrow_right.svg" alt="" class="h-6 w-6" /> </a> </div> </div> </div> </section> Pernah nggak sih kamu bingung mau pilih tempat makan atau booking restoran, tapi semua kelihatan sama aja? Nah, di sinilah kita beda! Bayangin kamu lagi cari tempat nongkrong yang nggak cuma enak makanannya, tapi juga punya extra diskon, bisa langsung datang tanpa nunggu, nggak ada biaya tambahan, dan pastinya tempatnya bersih kinclong kayak baru dipel tiap 5 menit. Semua benefit ini kita siapin biar kamu bisa dapetin pengalaman terbaik, tanpa ribet dan tanpa biaya yang bikin dompet meringis. Dan yang bikin tampilannya enak dilihat? Semua kontennya dibungkus dalam card layout yang elegan dan responsif pakai Tailwind CSS. Class kayak w-[315px], rounded-3xl, dan gap-[54px] bikin tiap kartu punya ukuran pas, sudut membulat yang modern, dan jarak antar elemen yang lapang banget. Setiap kartu ini juga punya tombol View Details di bawahnya yang udah dikasih transisi halus (transition-all duration-300) jadi saat kamu hover, warnanya langsung berubah jadi lebih hidup (thanks to hover:bg-primary). Terus, biar ikon di atas judul kelihatan standout, kita masukin ke dalam lingkaran transparan (bg-primary/15) dengan tinggi-lebar 80px, yang bikin elemen visualnya jadi lebih "ngomong" tanpa bikin layout rame. Semuanya disusun pakai utility-first class dari Tailwind nggak pakai CSS tambahan yang ribet. Hasilnya? Tampilan bersih, struktur rapi, dan pastinya performa juga ringan. Desainnya udah disetting secara fleksibel dalam flex flex-col dan gap antar elemen yang bikin antar bagian nggak dempet-dempetan. Jadi kalau kamu lihat ini, bukan cuma enak dibaca, tapi juga feel-nya profesional dan friendly banget. Popular restaurant Resto - Popular restaurant Section <section class="flex justify-center"> <div class="flex items-center w-[1320px] m-[60px]"> <div class="w-[367px]"> <div class="flex flex-col gap-1.5"> <span class="text-[16px] leading-6 font-semibold text-secondary uppercase">Top 3 Featured Restaurant</span> <h2 class="w-full text-[28px] leading-[42px] font-semibold">Most Popular Restaurants</h2> </div> <p class="mt-5 w-[360px] pr-5 text-[16px] leading-6 text-muted">The best restaurant in our opinion is how much customers like it in terms of place, price, comfort and of course the taste of the food itself.</p> <a href="#" class="mt-14 flex items-center justify-center text-[16px] leading-6 font-semibold h-[56px] w-[238px] bg-primary rounded-xl transition-all duration-300 hover:shadow-primary"> <span>View All Restaurant</span> <img src="./assets/images/icons/arrow_right.svg" alt="" class="h-6 w-6" /> </a> </div> <div class="ml-[85px] flex gap-5"> <!-- Card 1 --> <div class="card group transition-all duration-300 relative h-[475px] w-[400px] rounded-3xl overflow-clip shrink-0 hover:w-[400px]" data-card="1"> <img src="./assets/images/thumbnails/resto-1.png" alt="" class="object-cover w-full h-full" /> <div class="card-info transition-all duration-300 delay-1000 ease-in-out flex gap-2 p-6 items-center absolute left-6 bottom-6 right-6 bg-white rounded-3xl"> <div class="flex flex-col gap-1 w-[260px]"> <div class="flex items-center"> <img src="./assets/images/icons/star.svg" alt="" class="h-auto w-[18px] -mt-[3px] mr-[3px]" /> <p class="text-[16px] leading-6 font-semibold text-star mr-1">5.0</p> <p class="text-[16px] leading-6 text-muted">(7.6K+)</p> </div> <h3 class="text-lg leading-[27px] font-semibold">Bind Balorant</h3> <div class="flex gap-1.5"> <img src="./assets/images/icons/Location.svg" alt="" class="h-6 w-6" /> <p class="text-[16px] leading-6 text-muted">Jakarta, Indonesia</p> </div> </div> <a href="#" class="flex items-center justify-center h-9 w-9 rounded-xl bg-primary shrink-0 transition-all duration-300 hover:shadow-primary"> <img src="./assets/images/icons/sign_right.svg" alt="" class="" /> </a> </div> </div> <!-- Card 2 --> <div class="card group transition-all duration-300 relative h-[475px] w-[214px] rounded-3xl overflow-clip shrink-0 hover:w-[400px]" data-card="2"> <img src="./assets/images/thumbnails/resto-2.png" alt="" class="object-cover h-full w-full" /> <div class="card-info transition-all duration-300 delay-1000 ease-in-out gap-2 p-6 items-center absolute left-6 bottom-6 right-6 bg-white rounded-3xl hidden"> <div class="flex flex-col gap-1 w-[260px]"> <div class="flex items-center"> <img src="./assets/images/icons/star.svg" alt="" class="h-auto w-[18px] -mt-[3px] mr-[3px]" /> <p class="text-[16px] leading-6 font-semibold text-star mr-1">4.8</p> <p class="text-[16px] leading-6 text-muted">(5.2K+)</p> </div> <h3 class="text-lg leading-[27px] font-semibold">Green Garden</h3> <div class="flex gap-1.5"> <img src="./assets/images/icons/Location.svg" alt="" class="h-6 w-6" /> <p class="text-[16px] leading-6 text-muted">Bandung, Indonesia</p> </div> </div> <a href="#" class="flex items-center justify-center h-9 w-9 rounded-xl bg-primary shrink-0 transition-all duration-300 hover:shadow-primary"> <img src="./assets/images/icons/sign_right.svg" alt="" class="" /> </a> </div> </div> <!-- Card 3 --> <div class="card group transition-all duration-300 relative h-[475px] w-[214px] rounded-3xl overflow-clip shrink-0 hover:w-[400px]" data-card="3"> <img src="./assets/images/thumbnails/resto-3.png" alt="" class="object-cover h-full w-full" /> <div class="card-info transition-all duration-300 delay-1000 ease-in-out gap-2 p-6 items-center absolute left-6 bottom-6 right-6 bg-white rounded-3xl hidden"> <div class="flex flex-col gap-1 w-[260px]"> <div class="flex items-center"> <img src="./assets/images/icons/star.svg" alt="" class="h-auto w-[18px] -mt-[3px] mr-[3px]" /> <p class="text-[16px] leading-6 font-semibold text-star mr-1">4.9</p> <p class="text-[16px] leading-6 text-muted">(6.8K+)</p> </div> <h3 class="text-lg leading-[27px] font-semibold">Sundanese Delight</h3> <div class="flex gap-1.5"> <img src="./assets/images/icons/Location.svg" alt="" class="h-6 w-6" /> <p class="text-[16px] leading-6 text-muted">Sunda, Indonesia</p> </div> </div> <a href="#" class="flex items-center justify-center h-9 w-9 rounded-xl bg-primary shrink-0 transition-all duration-300 hover:shadow-primary"> <img src="./assets/images/icons/sign_right.svg" alt="" class="" /> </a> </div> </div> </div> </div> </section> Kamu pernah lihat deretan restoran yang langsung bikin kamu pengen klik salah satunya? Nah, bagian ini tuh kayak etalase yang ditata rapi dan glowing biar kamu tinggal pilih tanpa mikir dua kali. Di sebelah kiri, ada teks pengantar yang fungsinya kayak MC: ngenalin kamu ke tiga restoran paling favorit versi kita. Dengan struktur flex justify-center dan container w-[1320px], semuanya udah diposisikan pas di tengah layar. Terus bagian infonya dikemas rapi dalam lebar w-[367px], lengkap dengan heading “Most Popular Restaurants” dan subjudul kecil buat narik perhatian mata. Oh, dan tombol “View All Restaurant” itu bukan tombol biasa udah dikasih animasi transisi hover:shadow-primary yang bikin efek glowing halus setiap kamu arahkan kursor, kayak ngajak kamu buat eksplor lebih jauh. Sekarang kita ke bagian yang seru tiga kartu restoran yang tampilannya keren banget. Bayangin ini kayak tiga showcase makanan, satu lagi besar di depan, dua lainnya ngintip di samping. Kartu pertama dikasih lebar penuh w-[400px], sedangkan dua lainnya lebih ramping w-[214px], tapi semuanya punya trik rahasia: hover interaktif. Begitu kamu arahkan mouse ke kartu mana pun, ukurannya langsung membesar jadi hover:w-[400px], seolah-olah bilang "eh, aku nih yang kamu cari!" Ini dimungkinkan karena kita pakai Tailwind transition-all duration-300, jadi semua perubahan ukuran terasa mulus. Tiap kartu juga punya lapisan info yang muncul dari bawah dengan absolute bottom-6 left-6 right-6 dan bg-white rounded-3xl, lengkap sama rating, lokasi, nama restoran, dan tombol masuk kecil berikon. Dan yang bikin makin ciamik, info dua kartu samping awalnya disembunyiin (hidden) dan baru ditampilin saat interaksi (nanti bisa dikontrol pakai JS atau toggle class di hover). Jadi kamu bisa bikin UX yang dinamis banget hanya dengan mainin kombinasi class Tailwind dan sedikit logika interaktif. Ini bukan cuma pamer visual, ini storytelling visual yang ngajak user buat penasaran, eksplor, dan akhirnya klik. Near you restaurant Resto - Near you restaurant Section <section class="flex justify-center"> <div class="flex flex-col gap-[34px] w-[1321px] m-[60px]"> <div class="flex w-full items-center justify-between"> <div class="flex flex-col gap-1.5"> <span class="text-[16px] leading-6 font-semibold text-secondary uppercase">Restaurant Based by City</span> <h2 class="w-full text-nowrap text-[28px] leading-[42px] font-semibold">Restaurant Near You</h2> </div> <div class="relative dropdown flex flex-col"> <button type="button" class="dropdown-button flex cursor-pointer gap-1.5 px-6 py-5 items-center h-[64px] w-[252px] rounded-xl bg-[#f2f2f2]"> <img src="./assets/images/icons/Location.svg" alt="" class="" /> <div class="flex"> <p class="text-[16px] leading-6 font-medium text-muted">Jakarta, Indonesia</p> <div class="flex items-center justify-center h-6 w-6"> <img src="./assets/images/icons/arrow_bottom.svg" alt="" class="dropdown-arrow transition-all duration-300" /> </div> </div> </button> <ul class="dropdown-menu z-50 opacity-0 transition-all duration-300 absolute top-full left-0 mt-2 min-w-[150px] flex-col gap-1.5 bg-white rounded-xl py-2.5 shadow-lg"> <li class="group"> <button type="button" class="flex cursor-pointer gap-1.5 px-6 py-5 items-center h-[64px] w-[252px] transition-all duration-300 hover:bg-[#f2f2f2]"> <img src="./assets/images/icons/Location.svg" alt="" class="" /> <div class="flex"> <p class="text-[16px] leading-6 font-medium text-muted">Bandung, Indonesia</p> </div> </button> </li> <li class="group"> <button type="button" class="flex cursor-pointer gap-1.5 px-6 py-5 items-center h-[64px] w-[252px] transition-all duration-300 hover:bg-[#f2f2f2]"> <img src="./assets/images/icons/Location.svg" alt="" class="" /> <div class="flex"> <p class="text-[16px] leading-6 font-medium text-muted">Bali, Indonesia</p> </div> </button> </li> <li class="group"> <button type="button" class="flex cursor-pointer gap-1.5 px-6 py-5 items-center h-[64px] w-[252px] transition-all duration-300 hover:bg-[#f2f2f2]"> <img src="./assets/images/icons/Location.svg" alt="" class="" /> <div class="flex"> <p class="text-[16px] leading-6 font-medium text-muted">Jember, Indonesia</p> </div> </button> </li> </ul> </div> </div> <div class="flex gap-5"> <!-- Item 1 --> <div class="relative h-[415px] w-[427px] rounded-3xl overflow-clip shrink-0"> <img src="./assets/images/thumbnails/near-1.png" alt="" class="object-cover w-full h-full" /> <div class="flex gap-[35px] p-6 items-center absolute left-6 bottom-[33px] right-6 bg-white rounded-3xl"> <div class="flex flex-col gap-1 w-[260px]"> <div class="flex items-center"> <img src="./assets/images/icons/star.svg" alt="" class="w-[18px] -mt-[3.5px] mr-[3px]" /> <p class="text-[16px] leading-6 font-semibold text-star mr-1">5.0</p> <p class="text-[16px] leading-6 text-muted">(6.6K+)</p> </div> <h3 class="text-lg leading-[27px] font-semibold">Batavia Restaurant</h3> <p class="text-lg font-semibold leading-6 text-muted">IDR 29.000 - IDR 259.999</p> </div> <a href="#" class="flex items-center justify-center h-9 w-9 rounded-xl bg-primary shrink-0 transition-all duration-300 hover:shadow-primary"> <img src="./assets/images/icons/sign_right.svg" alt="" class="" /> </a> </div> </div> <!-- Item 2 --> <div class="relative h-[415px] w-[427px] rounded-3xl overflow-clip shrink-0"> <img src="./assets/images/thumbnails/near-2.png" alt="" class="object-cover w-full h-full" /> <div class="flex gap-[35px] p-6 items-center absolute left-6 bottom-[33px] right-6 bg-white rounded-3xl"> <div class="flex flex-col gap-1 w-[260px]"> <div class="flex items-center"> <img src="./assets/images/icons/star.svg" alt="" class="w-[18px] -mt-[3.5px] mr-[3px]" /> <p class="text-[16px] leading-6 font-semibold text-star mr-1">5.0</p> <p class="text-[16px] leading-6 text-muted">(3.6K+)</p> </div> <h3 class="text-lg leading-[27px] font-semibold">Split Ascent Restaurant</h3> <p class="text-lg font-semibold leading-6 text-muted">IDR 49.999 - IDR 560.000</p> </div> <a href="#" class="flex items-center justify-center h-9 w-9 rounded-xl bg-primary shrink-0 transition-all duration-300 hover:shadow-primary"> <img src="./assets/images/icons/sign_right.svg" alt="" class="" /> </a> </div> </div> <!-- Item 3 --> <div class="relative h-[415px] w-[427px] rounded-3xl overflow-clip shrink-0"> <img src="./assets/images/thumbnails/near-3.png" alt="" class="object-cover w-full h-full" /> <div class="flex gap-[35px] p-6 items-center absolute left-6 bottom-[33px] right-6 bg-white rounded-3xl"> <div class="flex flex-col gap-1 w-[260px]"> <div class="flex items-center"> <img src="./assets/images/icons/star.svg" alt="" class="w-[18px] -mt-[3.5px] mr-[3px]" /> <p class="text-[16px] leading-6 font-semibold text-star mr-1">5.0</p> <p class="text-[16px] leading-6 text-muted">(11K+)</p> </div> <h3 class="text-lg leading-[27px] font-semibold">Daza Fracture Restaurant</h3> <p class="text-lg font-semibold leading-6 text-muted">IDR 29.999 - IDR 560.000</p> </div> <a href="#" class="flex items-center justify-center h-9 w-9 rounded-xl bg-primary shrink-0 transition-all duration-300 hover:shadow-primary"> <img src="./assets/images/icons/sign_right.svg" alt="" class="" /> </a> </div> </div> </div> <a href="#" class="mt-[22px] mx-auto flex items-center justify-center h-14 w-[238px] bg-primary rounded-xl text-[16px] leading-6 font-semibold transition-all duration-300 hover:shadow-primary"> <span>View All Restaurant</span> <img src="./assets/images/icons/arrow_right.svg" alt="" class="h-6 w-6" /> </a> </div> </section> <section> ini kerja kayak wrapper utama yang pake Flexbox buat nge-center semua konten secara horizontal. Di dalamnya ada sebuah <div> container yang diatur pakai flex-col biar isinya berdiri vertikal dengan jarak (gap) 34px antar elemen, kayak kamu ngatur layout pake CSS Flexbox supaya rapi dan responsif. Ukuran lebar container juga fixed di 1321px dan ada margin 60px buat kasih ruang di sekitar, jadi tampilannya nggak nempel ke tepi layar. Bagian header-nya diatur pake Flexbox juga, justify-between supaya judul di kiri dan dropdown lokasi di kanan bisa “menjauh” saling berjauhan. Dropdown ini pakai positioning relative dan absolute buat dropdown menunya, yang awalnya opacity-0 dan disembunyiin, lalu muncul dengan animasi transisi halus (transition-all duration-300) saat tombol diklik. Tombolnya sendiri dikasih padding, rounded corners, dan background abu-abu muda buat kasih kesan tombol interaktif yang ramah buat user. Nah, bawahnya ada tiga card restoran yang ukurannya fixed (lebar 427px, tinggi 415px), tiap card pakai overflow-clip dan rounded-3xl supaya gambar dan konten gak keluar batas dan tampilannya jadi elegan. Gambar restoran dipasang dengan object-cover supaya memenuhi kotak tanpa nge-distorsi. Di bagian bawah tiap kartu, info restoran dikemas dalam flex dengan jarak antar elemen yang pas, plus tombol action kecil yang diberi efek hover shadow buat interaksi lebih hidup. Terakhir ada tombol “View All Restaurant” yang desainnya eye-catching, dengan ukuran fixed dan efek hover untuk meningkatkan user engagement. Expert chef Resto - Expert chef Section <section class="mt-[60px] mb-[123px] flex justify-center"> <div class="flex items-center gap-[64px] w-[1321px]"> <!-- Tab content --> <div class="tab-content relative h-[848px] w-[660px]"> <div class="absolute left-[60px] right-10 h-full w-[560px] rounded-3xl overflow-clip"> <img src="./assets/images/thumbnails/chef-1.png" alt="" class="object-cover w-full h-full" loading="lazy" /> </div> <div class="absolute top-10 right-0 px-[46px] py-[30px] flex flex-col items-center h-[203px] w-[179px] bg-white rounded-3xl drop-shadow-custom"> <div class="mb-3 flex items-center justify-center h-20 w-20 bg-[#FFF7ED] rounded-full"> <img src="./assets/images/icons/shield.svg" alt="" class="h-[50px] w-[50px] shrink-0" /> </div> <div class="w-[87px] text-center"> <p class="experience-years text-lg leading-[27px] font-semibold">12 Years</p> <p class="text-[16px] leading-6 text-muted">Experience</p> </div> </div> <div class="p-6 absolute left-0 bottom-10 flex items-center gap-3 h-auto w-[330px] rounded-3xl bg-white drop-shadow-custom2"> <div class="h-[132px] w-[96px] rounded-xl overflow-clip shrink-0"> <img src="./assets/images/thumbnails/resto-4.png" alt="" class="object-cover w-full h-full" /> </div> <div class="w-[174px] h-auto"> <div class="flex flex-col gap-6"> <div class="flex flex-col gap-1"> <span class="text-[16px] leading-6 text-muted">Chef at restaurant:</span> <p class="resto-name text-lg leading-[27px] font-semibold">Pearl Dolphin</p> </div> <a href="#" class="flex text-[16px] leading-6 font-semibold text-primary"> <span>View Details</span> <div class="flex items-center justify-center h-6 w-6 mask-[url('./assets/images/icons/arrow_right.svg')] mask-no-repeat mask-contain bg-primary"> <img src="./assets/images/icons/arrow_right.svg" alt="" class="opacity-0" /> </div> </a> </div> </div> </div> </div> <div class="flex flex-col gap-8 w-[596px]"> <div class="flex flex-col gap-1"> <span class="text-[18px] leading-6 font-semibold text-secondary uppercase">Top 4 Expert Chefs</span> <h2 class="w-full text-nowrap text-[32px] leading-[48px] font-semibold">Expert Chefs In Fuddy</h2> </div> <!-- Tab menu --> <div class="tab-menu flex flex-col gap-5"> <!-- Tab item 1 --> <div class="tab-item cursor-pointer p-6 flex flex-col gap-1.5 h-[141px] w-[596px] rounded-3xl group transition-all duration-300 hover:bg-white"> <span class="text-[16px] leading-6 font-medium text-muted uppercase">Pizza</span> <div class="w-full flex items-center justify-between"> <h2 class="text-[22px] leading-[33px] font-semibold line-clamp-1 mr-0.5">Phoenix Satcheup</h2> <a href="#" class="flex text-[16px] opacity-100 leading-6 font-medium text-primary shrink-0"> <span>Profile Details</span> <div class="flex items-center justify-center h-6 w-6 mask-[url('./assets/images/icons/arrow_right.svg')] mask-no-repeat mask-contain bg-primary shrink-0"> <img src="./assets/images/icons/arrow_right.svg" alt="" class="opacity-0" /> </div> </a> </div> <div class="flex items-center gap-1.5"> <div class="location-icon transition-all duration-300 mask-[url('./assets/images/icons/Location.svg')] mask-no-repeat mask-contain"> <img src="./assets/images/icons/Location.svg" alt="" class="h-6 w-6 opacity-0" /> </div> <p class="text-[16px] leading-6 text-muted">Jakarta, Indonesia</p> </div> </div> <!-- Tab item 2 --> <div class="tab-item cursor-pointer p-6 flex flex-col gap-1.5 h-[141px] w-[596px] rounded-3xl group transition-all duration-300 hover:bg-white"> <span class="text-[16px] leading-6 font-medium text-muted uppercase">Vegan</span> <div class="w-full flex items-center justify-between"> <h2 class="text-[22px] leading-[33px] font-semibold line-clamp-1 mr-0.5">Chamber Botfrag</h2> <a href="#" class="opacity-0 flex text-[16px] leading-6 font-medium text-primary shrink-0"> <span>Profile Details</span> <div class="flex items-center justify-center h-6 w-6 mask-[url('./assets/images/icons/arrow_right.svg')] mask-no-repeat mask-contain bg-primary shrink-0"> <img src="./assets/images/icons/arrow_right.svg" alt="" class="opacity-0" /> </div> </a> </div> <div class="flex items-center gap-1.5"> <div class="location-icon transition-all duration-300 mask-[url('./assets/images/icons/Location.svg')] mask-no-repeat mask-contain bg-foreground"> <img src="./assets/images/icons/Location.svg" alt="" class="h-6 w-6 opacity-0" /> </div> <p class="text-[16px] leading-6 text-muted">Bandung, Indonesia</p> </div> </div> <!-- Tab item 3 --> <div class="tab-item cursor-pointer p-6 flex flex-col gap-1.5 h-[141px] w-[596px] rounded-3xl group transition-all duration-300 hover:bg-white"> <span class="text-[16px] leading-6 font-medium text-muted uppercase">Roast Chicken</span> <div class="w-full flex items-center justify-between"> <h2 class="text-[22px] leading-[33px] font-semibold line-clamp-1 mr-0.5">Asep Vandal</h2> <a href="#" class="opacity-0 flex text-[16px] leading-6 font-medium text-primary shrink-0"> <span>Profile Details</span> <div class="flex items-center justify-center h-6 w-6 mask-[url('./assets/images/icons/arrow_right.svg')] mask-no-repeat mask-contain bg-primary"> <img src="./assets/images/icons/arrow_right.svg" alt="" class="opacity-0" /> </div> </a> </div> <div class="flex items-center gap-1.5"> <div class="location-icon transition-all duration-300 mask-[url('./assets/images/icons/Location.svg')] mask-no-repeat mask-contain bg-foreground shrink-0"> <img src="./assets/images/icons/Location.svg" alt="" class="h-6 w-6 opacity-0" /> </div> <p class="text-[16px] leading-6 text-muted">Sunda, Indonesia</p> </div> </div> <!-- Tab item 4 --> <div class="tab-item cursor-pointer p-6 flex flex-col gap-1.5 h-[141px] w-[596px] rounded-3xl group transition-all duration-300 hover:bg-white"> <span class="text-[16px] leading-6 font-medium text-muted uppercase">Beef Steak</span> <div class="w-full flex items-center justify-between"> <h2 class="text-[22px] leading-[33px] font-semibold line-clamp-1 mr-0.5">I Made Invoker</h2> <a href="#" class="opacity-0 flex text-[16px] leading-6 font-medium text-primary shrink-0"> <span>Profile Details</span> <div class="flex items-center justify-center h-6 w-6 mask-[url('./assets/images/icons/arrow_right.svg')] mask-no-repeat mask-contain bg-primary"> <img src="./assets/images/icons/arrow_right.svg" alt="" class="opacity-0" /> </div> </a> </div> <div class="flex items-center gap-1.5"> <div class="location-icon transition-all duration-300 mask-[url('./assets/images/icons/Location.svg')] mask-no-repeat mask-contain bg-foreground shrink-0"> <img src="./assets/images/icons/Location.svg" alt="" class="h-6 w-6 opacity-0" /> </div> <p class="text-[16px] leading-6 text-muted">Bali, Indonesia</p> </div> </div> </div> <a href="#" class="mt-[30px] w-fit flex items-center justify-center text-[16px] leading-6 font-semibold px-7 py-4 bg-primary rounded-xl transition-all duration-300 hover:shadow-primary"> <span>View All Chef</span> <div class="flex items-center justify-center h-6 w-6 shrink-0"> <img src="./assets/images/icons/arrow_right.svg" alt="" class="" /> </div> </a> </div> </div> </section> Kamu lihat di <section> ini, layoutnya tuh pakai Flexbox buat nge-center konten utama, jadi semuanya rapi di tengah layar. Di dalamnya ada dua bagian utama yang ukurannya fixed banget: satu sisi buat tampilin gambar chef yang kece dengan style absolute dan overflow-clip supaya gambarnya ngga keluar batas rounded-3xl, dan di atas gambar itu ada kartu info pengalaman chef yang dikasih drop shadow dan rounded buat bikin kesan keren dan profesional banget. Nah, gambar dan info ini diatur pakai position: absolute supaya bisa layering yang cakep, kayak main layer di design UI. Di sebelah kanan, kamu punya bagian tab menu yang isinya list chef-chef top yang keren banget. Masing-masing tab item itu clickable dengan efek hover halus (transition-all duration-300 hover:bg-white), pakai flex buat ngatur isi seperti nama chef, jenis masakan, dan lokasi dengan ikon location yang di-mask supaya tampilannya elegan. Teknik line-clamp-1 juga dipakai buat ngebatesin teks supaya ngga kelewat panjang dan tetap rapi tanpa pecah layout. Ini kayak kamu ngatur UI supaya user gampang scroll dan fokus ke satu chef tanpa kebingungan. Terakhir, ada tombol “View All Chef” yang desainnya eye-catching, dengan background bg-primary, padding dan rounded corners, plus efek shadow saat hover biar tombolnya kayak manggil-manggil kamu buat klik. Tombol ini pake flexbox juga supaya teks dan icon panahnya sejajar rapi. Intinya, struktur dan stylingnya padu banget pakai teknik CSS modern kayak flex, positioning absolute, mask, dan transition biar user experience kamu makin smooth dan keren. Kode JavaScript card.js document.addEventListener("DOMContentLoaded", () => { const cards = document.querySelectorAll(".card"); // Fungsi untuk mengatur lebar dan tampilan card info function updateCards() { cards.forEach((card) => { const cardInfo = card.querySelector(".card-info"); if (card.classList.contains("active")) { card.style.width = "400px"; cardInfo.classList.remove("hidden"); cardInfo.classList.add("flex"); } else { card.style.width = "214px"; cardInfo.classList.remove("flex"); cardInfo.classList.add("hidden"); } }); } // Inisialisasi card pertama sebagai aktif cards[0].classList.add("active"); updateCards(); // Tambahkan event listener untuk hover cards.forEach((card) => { card.addEventListener("mouseenter", () => { cards.forEach((c) => c.classList.remove("active")); card.classList.add("active"); updateCards(); }); card.addEventListener("mouseleave", () => { cards.forEach((c) => c.classList.remove("active")); cards[0].classList.add("active"); // Kembalikan ke card pertama updateCards(); }); }); }); Kode ini kayak bikin sebuah “slider” atau “carousel” kartu yang interaktif. Begitu halaman kamu selesai dimuat (DOMContentLoaded), skrip ini bakal cari semua elemen dengan kelas .card. Nah, tiap card itu nanti bisa “aktif” atau “nggak aktif” yang aktif ukurannya lebih gede (400px) dan bakal nampilin info tambahan ( .card-info ) pakai class flex, sedangkan yang nggak aktif ukurannya kecil (214px) dan info-nya disembunyiin dengan class hidden. Jadi ini kayak kamu lagi main highlight di antara beberapa kartu, yang lagi dipilih bakal lebih stand out dan jelas tampilannya. Terus, biar makin keren, tiap kali kamu hover (alias mouse masuk ke card), event listener mouseenter bakal otomatis ngehapus class active di semua card, terus pasang lagi di card yang kamu hover itu. Jadi ukurannya langsung nge-resize dinamis, pakai updateCards() yang nge-handle perubahan width dan tampilan info-nya secara real-time. Nah, kalau mouse keluar dari card (mouseleave), script ini bakal balikin highlight ke card pertama kayak reset posisi supaya user nggak bingung, tetap ada yang aktif terus. Jadi, kalau kamu pikir ini kayak efek “spotlight” di panggung, di mana spotlight cuma fokus ke satu aktor (card) sekaligus, dan aktor yang lain jadi lebih kecil sambil informasi detailnya disembunyiin. Kode ini ngandelin DOM manipulation dan classList API buat ngatur class CSS dan style inline langsung. Simple tapi powerful buat bikin UI yang responsif dan asik dipakai. dropdown.js document.addEventListener("DOMContentLoaded", () => { // Ambil semua tombol dropdown, menu, dan panah const dropdownButtons = document.querySelectorAll(".dropdown-button"); const dropdownMenus = document.querySelectorAll(".dropdown-menu"); const dropdownArrows = document.querySelectorAll(".dropdown-arrow"); // Fungsi untuk menangani klik pada tombol dropdown dropdownButtons.forEach((button, index) => { const menu = dropdownMenus[index]; const arrow = dropdownArrows[index]; button.addEventListener("click", () => { if (menu.classList.contains("opacity-0")) { // Sembunyikan semua dropdown lainnya dropdownMenus.forEach((m) => { m.classList.add("opacity-0"); m.classList.remove("opacity-100"); }); dropdownArrows.forEach((a) => (a.style.transform = "rotate(0deg)")); // Tampilkan dropdown yang dipilih menu.classList.remove("opacity-0"); menu.classList.add("opacity-100"); arrow.style.transform = "rotate(180deg)"; } else { // Sembunyikan dropdown saat diklik lagi menu.classList.remove("opacity-100"); menu.classList.add("opacity-0"); arrow.style.transform = "rotate(0deg)"; } }); }); // Fungsi untuk menutup dropdown saat mengklik di luar document.addEventListener("click", (event) => { dropdownButtons.forEach((button, index) => { const menu = dropdownMenus[index]; const arrow = dropdownArrows[index]; if (!button.contains(event.target) && !menu.contains(event.target)) { menu.classList.remove("opacity-100"); menu.classList.add("opacity-0"); arrow.style.transform = "rotate(0deg)"; } }); }); }); bayangin kamu lagi main sulap dengan dropdown di web, nah kode ini adalah “sihir” yang bikin dropdown-mu hidup! Jadi, pas halaman udah kelar dimuat (DOMContentLoaded), script ini bakal ngambil semua tombol dropdown (.dropdown-button), menu dropdown (.dropdown-menu), sama ikon panahnya (.dropdown-arrow). Intinya, tiap kali kamu klik tombol dropdown, script ini ngecek apakah menu dropdown-nya lagi tersembunyi atau nggak, pakai class CSS opacity-0 buat sembunyiin dan opacity-100 buat nampilin dengan efek transparansi. Jadi dropdownnya kayak magic muncul dan hilang. Terus, supaya nggak ribet dan rapi, kode ini juga bikin aturan kalau kamu klik satu tombol, semua dropdown lain langsung ditutup dulu. Ini biar gak ada yang buka banyak-banyak sekaligus dan bikin halaman jadi berantakan. Selain itu, ikon panahnya juga ikutan muter 180 derajat pas dropdown kebuka, kayak tanda panah lagi “ngasih kode” ke kamu, ini pakai manipulasi CSS transform: rotate(). Jadi gak cuma dropdown-nya yang interaktif, tapi ikon panahnya juga ikut ngejalanin animasi biar user experience-nya makin keren. Nah, ada juga bagian yang jenius nih, kalau kamu klik di luar dropdown atau tombolnya, dropdown otomatis nutup sendiri. Ini pake event listener di document yang ngecek target klik kamu, kalau kliknya bukan di tombol atau menu dropdown, ya semua dropdown langsung disembunyikan dan panah diputer balik lagi. Jadi kode ini pinter banget jaga-jaga supaya dropdown gak nyangkut kebuka terus, bikin UI kamu rapi dan enak dipakai. Jadi kamu bisa bilang, ini script dropdown-mu yang kerja kayak asisten pribadi, selalu siap buka tutup menu pas kamu butuh. main.js document.addEventListener("DOMContentLoaded", function () { new Swiper(".swiper", { slidesPerView: "auto", spaceBetween: 40, grabCursor: true, centeredSlides: false, }); }); bayangin kamu lagi nyusun slide foto atau konten keren yang bisa digeser-geser di halaman web, nah kode ini tuh yang jadi “otak” di balik layar itu! Jadi pas halaman udah siap (DOMContentLoaded), kamu langsung bikin instance baru dari Swiper, library JavaScript yang super powerfull buat bikin slider yang gampang dikendalikan. Dengan konfigurasi kayak slidesPerView: "auto", setiap slide bakal otomatis sesuaikan lebarnya, jadi gak kaku! Terus jarak antar slide diatur 40 piksel (spaceBetween: 40) biar slide-slide itu gak numpuk, ada ruang napas yang enak buat mata. Selain itu, ada fitur grabCursor: true yang bikin kursor kamu berubah jadi tangan siap “nangkep” slide, jadi interaksinya berasa lebih natural dan seru. Terus, centeredSlides: false bikin slide mulai dari kiri, bukan di tengah, jadi tampilannya kayak barisan slide biasa yang bisa kamu geser dengan gampang. Intinya, kode ini bikin slider kamu jadi responsif, interaktif, dan stylish cuma dengan beberapa baris aja. Simpel, tapi powerful. tab.js document.addEventListener("DOMContentLoaded", () => { const tabContents = [ { chefName: "Phoenix Satcheup", category: "Pizza", location: "Jakarta, Indonesia", experience: "12 Years", restaurant: "Pearl Dolphin", restaurantImage: "./assets/images/thumbnails/resto-4.png", chefImage: "./assets/images/thumbnails/chef-1.png", }, { chefName: "Chamber Botfrag", category: "Vegan", location: "Bandung, Indonesia", experience: "8 Years", restaurant: "Green Garden", restaurantImage: "./assets/images/thumbnails/resto-3.png", chefImage: "./assets/images/thumbnails/chef-2.webp", }, { chefName: "Asep Vandal", category: "Roast Chicken", location: "Sunda, Indonesia", experience: "10 Years", restaurant: "Sundanese Delight", restaurantImage: "./assets/images/thumbnails/resto-2.png", chefImage: "./assets/images/thumbnails/chef-3.webp", }, { chefName: "I Made Invoker", category: "Beef Steak", location: "Bali, Indonesia", experience: "15 Years", restaurant: "Bali Bistro", restaurantImage: "./assets/images/thumbnails/resto-1.png", chefImage: "./assets/images/thumbnails/chef-4.webp", }, ]; const tabs = document.querySelectorAll(".tab-item"); function updateTabContent(index) { const selectedContent = tabContents[index]; const chefImageElement = document.querySelector(".tab-content img"); if (chefImageElement) chefImageElement.src = selectedContent.chefImage; const chefNameElement = document.querySelector(".tab-content h2"); if (chefNameElement) chefNameElement.textContent = selectedContent.chefName; const experienceElement = document.querySelector(".experience-years"); if (experienceElement) experienceElement.textContent = selectedContent.experience; const restaurantNameElement = document.querySelector(".resto-name"); if (restaurantNameElement) restaurantNameElement.textContent = selectedContent.restaurant; const restaurantImageElement = document.querySelector( ".tab-content .h-[132px] img" ); if (restaurantImageElement) restaurantImageElement.src = selectedContent.restaurantImage; } function setActiveTab(index) { tabs.forEach((tab) => { tab.classList.remove("bg-white"); const profileDetailsLink = tab.querySelector("a"); if (profileDetailsLink) { profileDetailsLink.classList.remove("opacity-100"); profileDetailsLink.classList.add("opacity-0"); } const locationIcon = tab.querySelector(".location-icon"); if (locationIcon) { locationIcon.classList.remove("bg-muted"); locationIcon.classList.add("bg-foreground"); } }); const activeTab = tabs[index]; activeTab.classList.add("bg-white"); const profileDetailsLink = activeTab.querySelector("a"); if (profileDetailsLink) { profileDetailsLink.classList.remove("opacity-0"); profileDetailsLink.classList.add("opacity-100"); } const locationIcon = activeTab.querySelector(".location-icon"); if (locationIcon) { locationIcon.classList.remove("bg-foreground"); locationIcon.classList.add("bg-muted"); } } tabs.forEach((tab, index) => { tab.addEventListener("click", () => { setActiveTab(index); updateTabContent(index); }); }); setActiveTab(0); updateTabContent(0); }); Bayangin kamu lagi lihat halaman profil chef yang keren-keren, terus kamu mau lihat detail tiap chef dengan klik tab-tab yang ada. Nah, di sini ada array tabContents yang berisi data lengkap tiap chef, mulai dari nama, kategori makanan, pengalaman kerja, sampai foto chef dan restorannya. Jadi, tiap kali kamu klik salah satu tab, fungsi updateTabContent langsung jalan untuk ngubah isi halaman sesuai data chef yang kamu pilih ganti gambar, nama, pengalaman, dan info resto tanpa reload halaman. Praktis banget, kayak kamu scroll Instagram tapi kontennya berubah cepat dan mulus. Terus, supaya kamu gak bingung tab mana yang aktif, ada fungsi setActiveTab yang nge-handle styling tab yang lagi kamu pilih. Jadi tab yang aktif bakal punya background putih, link profilnya muncul dengan opacity penuh, dan ikon lokasi juga berubah warna supaya kamu gampang lihat mana yang sedang dipilih. Semua ini diatur dengan nambahin dan ngurangin kelas CSS secara dinamis pakai JavaScript, jadi tampilannya selalu rapi dan keren sesuai interaksi kamu. Gak cuma itu, setiap tab punya event listener yang siap nangkep klik kamu dan langsung nge-trigger dua fungsi tadi update konten dan set styling aktif. Jadi, kode ini kerja sama kayak DJ yang nyalain playlist sesuai request kamu, bikin pengalaman browsing info chef jadi asik, interaktif, dan smooth banget tanpa harus ribet reload halaman. Pokoknya, kamu tinggal klik, langsung dapet info yang kamu mau dengan tampilan yang kece. input.css @import url("<https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap>"); @import "tailwindcss"; :root { --background: #fafafa; --foreground: #232631; --primary: #fdc886; --secondary: #5a4fcf; --muted: #656565; --star: #ffb800; --placeholder: #858585; } @theme inline { --font-poppins: "Poppins", sans-serif; --color-background: var(--background); --color-foreground: var(--foreground); --color-primary: var(--primary); --color-secondary: var(--secondary); --color-muted: var(--muted); --color-star: var(--star); --color-placeholder: var(--placeholder); --drop-shadow-custom: 8px 8px 25px rgba(102, 102, 102, 0.04); --drop-shadow-custom2: -8px 8px 25px rgba(102, 102, 102, 0.04); --shadow-primary: 0 4px 10px 0 rgba(253, 200, 134, 0.8); --shadow-secondary: 0 4px 10px 0 rgba(90, 79, 207, 0.1); } @layer base { body { @apply bg-background text-foreground font-poppins; } } Bayangin kamu lagi nyiapin wardrobe keren buat website kamu. Di sini, kamu lagi impor font Poppins dari Google Fonts, jadi tipografi di halaman bakal tampil stylish dan modern banget kayak kamu lagi pilih outfit kece dari brand terkenal supaya tampil maksimal. Trus kamu juga impor Tailwind CSS, yang kayak toolkit super lengkap buat styling cepat dan rapi tanpa harus pusing bikin CSS dari nol. Nah, di bagian :root, kamu bikin semacam ‘warna-warna andalan’ yang nanti bisa dipakai berulang-ulang di seluruh website. Jadi, misalnya kamu mau pakai warna utama buat tombol atau background, tinggal panggil --primary aja. Ini kayak kamu punya palette warna khusus yang selalu kamu pakai biar desainnya konsisten dan gak bikin mata lelah. Ada juga warna khusus buat placeholder, shadow keren, dan warna-warna muted yang bikin tampilan makin dinamis dan enak dilihat. Terus, di bagian @theme inline kamu bikin varian variabel CSS yang nyambung ke warna dan font tadi, plus beberapa efek shadow custom yang bikin elemen di halaman kayak “ngambang” dengan efek cahaya lembut. Nah, terakhir, di @layer base kamu pakai directive @apply dari Tailwind untuk langsung set style dasar body: background, warna teks, dan font default dari Poppins itu. Jadi, intinya kamu udah bikin fondasi styling yang solid dan mudah dikembangkan tanpa ribet, biar website kamu tampil clean, konsisten, dan super profesional. .gitignore node_modules Gitignore dengan isi node_modules itu ibarat kamu punya lemari yang super gede penuh barang, tapi kamu gak mau semua barang itu ikut kamu bawa ke rumah teman saat nongkrong. Jadi, file node_modules itu tempatnya semua paket dan dependensi yang kamu download dari npm, ukurannya bisa super jumbo dan isinya bisa direbuild kapan aja dari package.json. Dengan nge-ignore folder ini di .gitignore, kamu ngasih tahu Git buat gak ikut nyimpen folder node_modules ke repository, supaya repo kamu tetap ringan, gak penuh sampah, dan proses push-pull jadi lebih cepat. Jadi intinya, folder node_modules itu gampang dibuat ulang, gak perlu dibawa-bawa ke remote git. package.json { "name": "resto", "version": "1.0.0", "description": "", "license": "ISC", "author": "", "type": "commonjs", "main": "/src/index.html", "scripts": { "dev": "npx @tailwindcss/cli -i ./src/input.css -o ./src/output.css --watch" }, "dependencies": { "@tailwindcss/cli": "^4.1.3", "tailwindcss": "^4.1.3" } } jadi ini adalah isi file package.json yang jadi otak dari proyek Node.js atau frontend kamu, khususnya yang pakai Tailwind CSS. Di sini, ada beberapa info penting seperti name proyek kamu (dalam hal ini "resto"), versi, dan lisensi. Nah, bagian scripts itu keren banget karena kamu bisa bikin perintah singkat buat jalanin hal-hal yang sering kamu lakukan. Misalnya, script dev ini bakal ngejalanin Tailwind CSS CLI secara otomatis, nge-compile file CSS input (input.css) ke output (output.css), dan yang paling asik, dia terus “watch” alias mantau perubahan di file input, jadi tiap kamu simpan perubahan, Tailwind langsung update CSS-nya tanpa kamu harus ngapa-ngapain lagi. Terus di bagian dependencies, itu tempat kamu nyimpen paket-paket yang dipakai proyek kamu, dalam hal ini ada @tailwindcss/cli dan tailwindcss versi 4.1.3. Jadi, setiap kali kamu atau temen kamu clone project ini, tinggal ketik npm install atau yarn buat otomatis download semua paket ini sesuai versi yang tertulis, biar semua lingkungan kerja sama dan gak ada yang versi beda-beda. Singkatnya, package.json ini semacam peta dan remote control buat ngatur seluruh dependency dan perintah penting dalam proyek kamu. vercel.json { "builds": [ { "src": "src/**/*", "use": "@vercel/static" } ], "routes": [ { "src": "/(.*)", "dest": "/src/$1" } ] } Ini konfigurasi buat Vercel yang ngatur gimana cara nge-deploy project kamu: bagian builds bilang kalau semua file di folder src bakal diproses sebagai konten statis pakai builder @vercel/static, jadi Vercel tahu harus ngirim file-file itu apa adanya ke server. Terus di bagian routes, setiap request URL yang masuk bakal diarahkan ke file yang sesuai di folder src, jadi misalnya kamu buka /about, dia bakal nyari file src/about buat ditampilin, intinya ini bikin routing simple dan bikin file di src bisa langsung diakses kayak di root website kamu. Cara Mengedit Buat ngedit sesuatu itu gampang banget, misalnya kamu pengen ubah teks “Why Should Choose Us?” di kodenya. Caranya simpel, kamu tinggal pencet Ctrl + F di VSCode, terus ketik keyword atau kata kunci yang mau kamu cari, misal “Why Should Choose Us?”. Nah, otomatis VSCode bakal langsung bawa kamu ke bagian kodenya yang isinya teks itu. Tinggal kamu ganti deh teksnya sesuai yang kamu mau, terus simpan. Setelah itu, coba reload atau refresh halaman di browser, dan kamu bakal lihat perubahan yang kamu buat langsung muncul tanpa ribet. Contohnya kayak gini: Resto - Video Tutorial How to Edit Beragam Template HTML + Tailwind CSS ( FREE )🙌 Shaynakit - HTML Template Nah, yang terakhir nih, kalau kamu pengen langsung dapetin template yang udah jadi lengkap sama HTML dan Tailwind CSS-nya, tinggal dipakai aja tanpa harus ribet ngoding dari nol, kamu bisa mampir ke situs Shaynakit. Di sana banyak banget pilihan template gratis yang keren-keren, tapi juga ada yang versi premium kalau kamu pengen yang lebih eksklusif. Template-nya juga lengkap banget, mulai dari desain buat mobile sampai desktop, jadi cocok buat apapun kebutuhan proyek kamu. Pokoknya tinggal daftar atau registrasi, terus pilih deh template mana yang kamu suka, download, dan langsung pakai di proyek kamu. Ini cara yang asik banget buat ngehemat waktu dan tetep dapet hasil desain yang kece tanpa harus pusing mikirin styling dari awal. Jadi, jangan ragu buat eksplor dan manfaatin sumber daya keren ini buat bikin proyek kamu makin mantap. Penutup Sebagai penutup, kamu sekarang sudah punya sumber kode HTML dan Tailwind CSS lengkap dengan contoh dan penjelasannya supaya gak bingung. Kalau kamu butuh template kece lainnya, tinggal cek aja situs Shaynakit yang penuh dengan desain dan kode siap pakai. Jadi, semangat terus belajar dan eksplorasi supaya kamu bisa bikin proyek web yang keren banget.

Kelas Tutorial Next JS 15 Belajar Server dan Client Components Projek Web Booking Hotel di BuildWithAngga

Tutorial Next JS 15 Belajar Server dan Client Components Projek Web Booking Hotel

Kalau kamu baru mau mulai belajar bikin website modern, Next.js 15 adalah salah satu framework yang wajib kamu lirik. Framework ini dibangun di atas React, tapi punya banyak fitur tambahan yang bikin proses bikin web jadi lebih simpel, lebih cepat, dan hasilnya juga lebih SEO-friendly. Jadi kalau kamu pengen bantuin bisnis online biar lebih mudah ditemukan di Google, atau pengen klien kamu puas karena websitenya performanya mantap, Next.js bisa jadi solusi yang tepat. Yang bikin Next.js 15 makin menarik adalah kemampuannya dalam urusan SEO dan performa. Kita tahu, dua hal ini penting banget buat bisnis online. SEO itu ngaruh ke berapa banyak orang bisa nemuin website kamu lewat pencarian, sementara performa itu ngaruh ke apakah orang betah atau nggak buka websitenya. Next.js 15 hadir buat meanjawab dua masalah itu secara elegan, karena dia bisa bantu kamu ngerender halaman langsung dari server (yang lebih cepet diakses dan dibaca mesin pencari), tapi juga bisa fleksibel saat kamu butuh interaksi dinamis dari sisi pengguna. Kenapa Harus Paham Perbedaan Server dan Client Component? Nah, kalau kamu baru banget mulai, mungkin istilah server component dan client component masih terdengar asing. Tapi percaya deh, ini salah satu hal paling penting yang harus kamu kuasai dulu sebelum ngoding lebih jauh di Next.js 15. Singkatnya, komponen server itu dijalankan di server, artinya data bisa diolah duluan sebelum dikirim ke browser. Hasilnya? Website kamu bisa tampil lebih cepat dan langsung ramah SEO karena kontennya udah siap ketika user mengakses. Sedangkan client component dijalankan di browser, cocok buat fitur-fitur interaktif yang butuh respon langsung dari user, seperti klik tombol, buka modal, atau update state. Kalau kamu asal pakai semuanya di client padahal bisa di-handle server, efeknya bisa bikin web kamu jadi lambat dan susah terbaca mesin pencari. Sebaliknya, kalau kamu maksa semua di server tapi butuh interaksi, hasilnya juga bisa nge-lag dan nggak responsif. Makanya penting banget untuk tahu kapan pakai yang mana. Kita Akan Belajar Perlahan, Jangan Takut Duluan Tenang aja, artikel ini dibuat buat kamu yang baru mulai banget. Kita bakal bahas perlahan, dari konsep dasarnya dulu, biar kamu nggak bingung. Kita akan pelan-pelan bongkar gimana cara kerja server dan client components di Next.js 15, kenapa ini penting banget buat performa dan SEO, dan kapan waktu terbaik buat pakai masing-masing. Tujuannya adalah supaya kamu punya pondasi yang kuat dulu. Karena kalau fondasi kamu udah paham, ke depannya mau bikin website e-commerce, portfolio, company profile, sampai SaaS platform, semua bakal lebih mudah. Artikel ini bukan cuma ngajarin ngoding doang, tapi juga ngajak kamu mikir sebagai developer yang ngerti kenapa harus begini, bukan cuma ikut-ikutan tutorial aja. Lanjut yuk ke bagian berikutnya, di mana kita mulai bahas bagaimana cara membedakan dan menggunakan server component dan client component dengan lebih praktis. Apa Itu Server Component di Next.js 15? Kalau kamu baru kenal Next.js 15, kamu pasti sering dengar istilah server component. Tapi sebenarnya, apa sih server component itu? Server component adalah komponen React yang dijalankan di server, bukan di browser user. Jadi, ketika user membuka halaman website kamu, server-lah yang lebih dulu menjalankan logika dan menghasilkan tampilan HTML-nya. Setelah hasil HTML itu siap, baru dikirim ke browser. Dengan cara ini, user langsung melihat halaman yang sudah siap tampil, tanpa perlu nunggu proses JavaScript di browser. Berbeda dengan client component yang perlu waktu tambahan untuk diload dan dijalankan di browser (karena harus menunggu JavaScript siap), server component itu ibaratnya udah nyiapin semua makanan sebelum diantar ke meja kamu. Kamu tinggal duduk, dan makanan langsung tersedia. Nggak perlu nunggu dimasak lagi di depan kamu. Next.js 15 mempermudah proses ini dengan pembagian otomatis mana komponen yang berjalan di server dan mana yang butuh interaksi dan harus berjalan di sisi klien (browser). Jadi kita sebagai developer bisa fokus membangun pengalaman pengguna yang cepat, nyaman, dan tetap SEO-friendly. Manfaat Utama dari Server Component Ngomongin soal manfaatnya, server component itu bisa dibilang “raja” dalam urusan kecepatan dan SEO. Kenapa? Karena dia bekerja secara efisien dari sisi server dan menghindari kerja ekstra di browser user. Berikut beberapa manfaat paling terasa: SEO Lebih Optimal Konten halaman sudah siap saat dikirim ke browser, jadi mesin pencari seperti Google bisa langsung membaca dan mengindeks kontennya dengan mudah. Cocok banget buat halaman yang butuh tampil di hasil pencarian, seperti halaman detail hotel, promo kamar, atau artikel blog.Waktu Loading Lebih Cepat Karena data langsung diproses dari server, user nggak perlu nunggu JavaScript di browser selesai dijalankan dulu. Halaman bisa tampil hampir seketika.Bundle Size Lebih Kecil Komponen yang dijalankan di server nggak perlu dikirim ke browser dalam bentuk JavaScript. Artinya, ukuran file yang perlu didownload user jadi lebih kecil. Ini bikin website kamu lebih ringan dan hemat kuota.Keamanan Lebih Baik Proses data penting atau sensitif seperti akses database, autentikasi, atau validasi bisa tetap dilakukan aman di sisi server. User nggak akan bisa ngintip atau manipulasi proses ini dari browser.Arsitektur Lebih Rapi Kamu bisa pisahkan dengan jelas mana komponen yang hanya bertugas menampilkan data, dan mana yang bertugas menerima input dari user. Ini bikin struktur kode kamu lebih mudah dipelihara. Analogi Simpel Supaya Lebih Paham Bayangin kamu lagi pengen booking hotel buat liburan ke Bali. Kamu punya dua pilihan cara untuk cari hotel: Cara pertama (Server Component): Kamu buka aplikasi travel dan langsung dapet halaman yang udah lengkap isinya — daftar hotel, harga per malam, fasilitas, dan ulasan pelanggan. Semua sudah ditata rapi dan langsung muncul begitu kamu buka. Enak kan?Cara kedua (Client Component): Kamu buka aplikasinya, tapi halaman awal kosong. Lalu perlahan-lahan isinya mulai muncul satu-satu — loading harga, loading nama hotel, loading gambar. Kamu nunggu semuanya siap sambil bengong. Nggak enak, apalagi kalau internet lemot. Nah, Next.js 15 dengan server component ngasih kamu pengalaman seperti cara pertama. Data hotelnya udah disiapkan dari awal. Pengguna tinggal lihat dan pilih. Contoh Koding Lengkap Halaman Hotel Sekarang kita lihat contoh real-nya di project Next.js 15. Bayangin kamu bikin halaman daftar hotel di file app/hotels/page.tsx. Komponen ini akan di-render di server dan siap tampil dengan cepat. // app/hotels/page.tsx import { getAllHotels } from '@/lib/hotel.api'; import Image from 'next/image'; export default async function HotelListPage() { const hotels = await getAllHotels(); // Fetch data langsung di server return ( <div className="max-w-5xl mx-auto py-12"> <h1 className="text-3xl font-bold mb-6">Temukan Hotel Terbaik untuk Liburanmu</h1> <p className="text-gray-600 mb-10">Berbagai pilihan hotel dengan fasilitas terbaik, lokasi strategis, dan harga yang bersahabat.</p> <div className="grid grid-cols-2 gap-6"> {hotels.map((hotel) => ( <div key={hotel.id} className="border rounded-xl p-4 shadow bg-white"> <Image src={hotel.image} alt={hotel.name} width={400} height={250} className="rounded-lg" /> <h2 className="text-xl font-semibold mt-4">{hotel.name}</h2> <p className="text-sm text-gray-600">{hotel.location}</p> <p className="mt-2 text-green-600 font-bold"> Rp {hotel.pricePerNight.toLocaleString('id-ID')} / malam </p> </div> ))} </div> </div> ); } Apa yang Terjadi di Koding Ini? Fungsi getAllHotels() akan ambil data dari database atau API, dan ini dilakukan langsung di server.Karena kita pakai async function, kita bisa langsung pakai await di dalam komponen. Ini fitur eksklusif buat server component.Hasil HTML dari komponen ini akan langsung dikirim ke browser, jadi user nggak nungguin data dimuat lewat JavaScript.Komponen ini nggak punya interaksi dinamis (kayak klik tombol atau input), jadi cocok dijalankan dari server. Dengan pendekatan ini, halaman daftar hotel kamu jadi lebih cepat terbuka, lebih SEO-friendly, dan lebih efisien untuk user. Cocok banget buat weeb booking hotel yang butuh kecepatan dan kepercayaan. Setelah paham server component, di bagian selanjutnya kita bakal bahas client component, bagian yang berperan besar dalam membuat halaman hotel jadi lebih interaktif dan user bisa berkomunikasi dengan aplikasi secara langsung. Apa Itu Client Component di Next.js 15? Setelah kamu kenalan dengan server component, sekarang saatnya kita bahas client component — pasangan penting dalam pengembangan aplikasi Next.js 15. Kalau server component dijalankan di server dan menghasilkan HTML siap tampil, client component adalah komponen yang dijalankan di browser user. Artinya, semua logika, state (data sementara), event handler seperti onClick, onChange, sampai interaksi real-time — semuanya terjadi di sisi klien (client-side). Dalam proyek website booking hotel, client component cocok banget digunakan untuk bagian interaktif, misalnya: Formulir pencarian hotel berdasarkan lokasi dan tanggalTombol “Lihat Detail” atau “Booking Sekarang”Fitur filter berdasarkan harga, rating, atau fasilitasModal popup yang muncul saat user klik sesuatuDropdown pemilihan jumlah tamu atau kamar Pokoknya, kalau kamu butuh interaksi dari user dan ingin websitenya terasa “hidup”, kamu bakal pakai client component. Manfaat Utama Client Component Bisa Tangani Interaksi Langsung dari User Client component memungkinkan kamu menambahkan event seperti klik tombol, input teks, validasi form, dan sebagainya. Hal-hal seperti ini nggak bisa dilakukan di server component.Penggunaan State yang Fleksibel Kamu bisa pakai state (misalnya dengan useState) untuk menyimpan data sementara, seperti isian form atau status toggle.Pengalaman Pengguna Lebih Interaktif Misalnya ketika user klik tombol untuk membuka popup, mengganti tanggal booking, atau melihat harga terbaru, kamu bisa langsung merespon tanpa reload halaman.Cocok untuk Komponen UI Kompleks Kalau kamu bikin komponen seperti carousel gambar, kalender booking, atau sidebar filter dinamis, semuanya butuh client component agar bisa jalan. Analogi Simpel Supaya Makin Paham Bayangin kamu lagi di hotel dan pengen pesan makanan dari kamar. Kalau kamu cuma nerima brosur (kayak server component), kamu tahu daftar makanannya, tapi kamu tetap harus kontak resepsionis buat order. Nah, kalau client component, itu seperti kamu dikasih tablet di kamar yang bisa langsung kamu pencet-pencet buat order, bayar, dan atur pengiriman ke kamar. Semua bisa kamu lakukan langsung dari situ — interaktif dan instan. Client component itu penting banget untuk interaksi seperti itu. Jadi kombinasi antara brosur (server component) dan tablet interaktif (client component) itulah yang bikin pengalaman pengguna jadi komplit. Contoh Koding Client Component di Next.js 15 Sekarang kita bikin contoh client component di halaman booking hotel, misalnya komponen pencarian hotel berdasarkan nama kota yang bisa langsung merespon input user. 'use client'; import { useState } from 'react'; export default function HotelSearchForm() { const [city, setCity] = useState(''); const [results, setResults] = useState([]); const handleSearch = async () => { const res = await fetch(`/api/search-hotels?city=${city}`); const data = await res.json(); setResults(data.hotels); }; return ( <div className="p-6 bg-white rounded-xl shadow-md max-w-xl mx-auto"> <h2 className="text-xl font-semibold mb-4">Cari Hotel Berdasarkan Kota</h2> <input type="text" value={city} onChange={(e) => setCity(e.target.value)} placeholder="Contoh: Bandung" className="w-full border px-4 py-2 rounded-md mb-4" /> <button onClick={handleSearch} className="bg-blue-600 text-white px-6 py-2 rounded-md" > Cari Hotel </button> {results.length > 0 && ( <div className="mt-6"> <h3 className="text-lg font-medium mb-2">Hasil Pencarian:</h3> <ul className="space-y-2"> {results.map((hotel: any) => ( <li key={hotel.id} className="border rounded-md p-3 hover:bg-gray-50 transition" > <p className="font-bold">{hotel.name}</p> <p className="text-sm text-gray-600">{hotel.location}</p> </li> ))} </ul> </div> )} </div> ); } Apa yang Terjadi di Komponen Ini? Baris ‘use client’; di paling atas wajib ditulis. Ini memberi tahu Next.js bahwa komponen ini harus dijalankan di sisi browser.Kita pakai useState untuk menyimpan input kota dan hasil pencarian.Saat user mengetik nama kota, datanya langsung disimpan tanpa reload halaman.Ketika tombol “Cari Hotel” ditekan, kita fetch data dari API, dan tampilkan hasilnya langsung di bawahnya.Semua ini hanya bisa dilakukan di client component, karena bergantung pada interaksi user secara langsung dan state yang berubah-ubah. Kapan Harus Pakai Client Component? Pakai client component kalau kamu butuh: Interaksi langsung (klik, input, validasi)State lokal yang berubah-ubahKomponen dinamis seperti dropdown, toggle, modalEfek animasi atau event pengguna (hover, scroll) Tapi tetap harus bijak. Jangan semua komponen dijadikan client component, karena itu bisa bikin bundle JavaScript kamu jadi besar dan berat. Pakai client component hanya untuk bagian yang memang butuh. Dengan menggabungkan server component untuk tampilan awal dan SEO, serta client component untuk interaksi pengguna, kamu bisa bikin web booking hotel yang cepat, canggih, dan tetap enak digunakan. Di bagian selanjutnya, kita akan lihat bagaimana menggabungkan dua pendekatan ini dalam satu halaman — biar websitemu makin optimal! Menggabungkan Server dan Client Component dalam Satu Halaman Setelah paham perbedaan dan fungsi masing-masing, sekarang saatnya kita bahas hal yang paling sering dilakukan di project nyata: menggabungkan server dan client component dalam satu halaman. Ini adalah pendekatan yang sangat umum di Next.js 15 — dan justru inilah kekuatannya. Bayangin kamu sedang bikin halaman utama untuk web booking hotel. Di bagian atas halaman, kamu ingin menampilkan daftar hotel secara statis berdasarkan data dari server (server component). Tapi di bagian bawahnya, kamu pengin kasih filter interaktif supaya user bisa cari hotel berdasarkan kota, harga, atau tanggal (client component). Nah, kombinasi kayak gini sangat ideal dan bikin pengalaman pengguna jadi lengkap. Kenapa Harus Digabung? Karena nggak semua bagian halaman perlu interaksi. Kalau semua kamu jadikan client component, websitenya jadi berat dan lambat. Tapi kalau semua server, user jadi nggak bisa ngapa-ngapain secara langsung. Dengan Next.js 15, kamu bisa dapat yang terbaik dari dua dunia: Server component bikin halaman cepat tampil dan SEO-friendlyClient component bikin interaksi lebih hidup dan personal Contoh Koding Kombinasi Server + Client Berikut ini contoh struktur file app/hotels/page.tsx yang merupakan server component utama, lalu di dalamnya kita import client component HotelSearchForm. // app/hotels/page.tsx (SERVER COMPONENT) import { getAllHotels } from '@/lib/hotel.api'; import HotelSearchForm from '@/components/HotelSearchForm'; import Image from 'next/image'; export default async function HotelListPage() { const hotels = await getAllHotels(); // Data diambil dari server return ( <div className="max-w-5xl mx-auto py-12"> <h1 className="text-3xl font-bold mb-6">Temukan Hotel Terbaik</h1> <div className="grid grid-cols-2 gap-6 mb-12"> {hotels.map((hotel) => ( <div key={hotel.id} className="border rounded-xl p-4 shadow bg-white"> <Image src={hotel.image} alt={hotel.name} width={400} height={250} className="rounded-lg" /> <h2 className="text-xl font-semibold mt-4">{hotel.name}</h2> <p className="text-sm text-gray-600">{hotel.location}</p> <p className="mt-2 text-green-600 font-bold"> Rp {hotel.pricePerNight.toLocaleString('id-ID')} / malam </p> </div> ))} </div> {/* Bagian Interaktif */} <HotelSearchForm /> </div> ); } Dan ini file components/HotelSearchForm.tsx sebagai client component: // components/HotelSearchForm.tsx (CLIENT COMPONENT) 'use client'; import { useState } from 'react'; export default function HotelSearchForm() { const [city, setCity] = useState(''); const [results, setResults] = useState([]); const handleSearch = async () => { const res = await fetch(`/api/search-hotels?city=${city}`); const data = await res.json(); setResults(data.hotels); }; return ( <div className="p-6 bg-white rounded-xl shadow-md"> <h2 className="text-xl font-semibold mb-4">Cari Hotel Berdasarkan Kota</h2> <input type="text" value={city} onChange={(e) => setCity(e.target.value)} placeholder="Contoh: Yogyakarta" className="w-full border px-4 py-2 rounded-md mb-4" /> <button onClick={handleSearch} className="bg-blue-600 text-white px-6 py-2 rounded-md" > Cari Hotel </button> {results.length > 0 && ( <ul className="mt-4 space-y-2"> {results.map((hotel: any) => ( <li key={hotel.id} className="border p-3 rounded-md shadow-sm"> <p className="font-bold">{hotel.name}</p> <p className="text-sm text-gray-500">{hotel.location}</p> </li> ))} </ul> )} </div> ); } Penjelasan Alurnya HotelListPage adalah server component. Jadi halaman ini bisa langsung render daftar hotel dengan cepat saat user mengakses.Di dalamnya kita menyisipkan HotelSearchForm, yang merupakan client component. Jadi bagian ini akan berjalan di browser.User bisa langsung input kota dan lihat hasil pencarian tanpa reload — karena bagian ini dinamis.Pendekatan ini memberikan first load yang cepat dan tetap mendukung interaktivitas real-time. Kesimpulan Menggabungkan server dan client component dalam satu halaman adalah hal yang sangat umum dan powerful di Next.js 15. Ini adalah cara paling optimal untuk membangun website modern: cepat, SEO-friendly, dan tetap interaktif. Jadi mulai sekarang, saat kamu bikin halaman apapun — entah itu homepage, booking form, atau katalog hotel — pikirkan dulu mana bagian yang bisa disajikan dari server, dan mana bagian yang perlu interaksi dari client. Dengan cara ini, web kamu akan terasa lebih profesional, lebih ringan, dan jauh lebih menyenangkan digunakan oleh pengunjung. Di artikel berikutnya, kita bisa lanjut bahas gimana caranya optimasi SEO dan performa lebih dalam lagi, sambil tetap mempertahankan user experience terbaik di Next.js 15. Penutup: Saatnya Naik Level Bareng Mentor Expert Kalau kamu sudah sampai di bagian akhir artikel ini, artinya kamu serius banget ingin memahami Next.js 15 dengan benar. Dan itu langkah yang keren banget. Tapi, jujur aja, belajar sendiri kadang bisa bikin frustasi. Banyak fitur baru, banyak istilah asing, kadang kita udah ngoding bener tapi hasilnya nggak sesuai harapan. Di sinilah belajar bareng mentor expert jadi solusi paling efektif. Bayangin kamu punya seseorang yang udah pernah ngerjain real project dengan Next.js, yang ngerti seluk-beluk client dan server components, yang tahu kesalahan umum pemula, dan bisa bantu kamu debug saat kamu stuck. Belajar jadi lebih cepat, lebih terarah, dan jauh lebih menyenangkan. Dengan ikut belajar bareng mentor: Kamu bisa dapet feedback langsung dari orang yang lebih berpengalamanAda komunitas belajar yang bikin kamu tetap semangat dan nggak merasa sendirianKamu bisa dapet template dan resource premium yang siap pakaiBelajar bukan cuma teori, tapi langsung bikin project nyata yang bisa dipakai buat portofolioDan yang paling penting: kamu bisa tanya apa aja tanpa takut di-judge Buat kamu yang pengen kerja sebagai frontend developer profesional, atau ingin ngebantu bisnis online dengan performa web yang lebih cepat dan SEO-friendly, belajar bareng mentor itu investasi terbaik yang bisa kamu ambil hari ini. Yuk, jangan cuma jadi penonton. Saatnya kamu sendiri yang bikin karya hebat. Belajar bareng mentor di BuildWithAngga, dan rasain sendiri bedanya ketika kamu punya support system yang solid. 🚀💻 Sampai ketemu di artikel dan kelas selanjutnya!

Kelas Tutorial Next JS 15 Belajar Image Optimization di BuildWithAngga

Tutorial Next JS 15 Belajar Image Optimization

Pernah gak kamu buka sebuah website, dan dalam 3 detik langsung tutup tab-nya karena tampilannya meh banget? Nah, hal kayak gitu tuh bisa berdampak besar buat bisnis online. Di dunia digital sekarang, kesan pertama pengguna ditentukan bukan dari kata-kata, tapi dari tampilan visual yang mereka lihat di layar. Bayangin kamu punya produk bagus, layanan keren, bahkan diskon besar-besaran… tapi desain websitenya berantakan, loading-nya lama, gambar-gambar pecah, dan warna-warnanya bikin mata sakit. Udah pasti banyak pengunjung yang bakal langsung kabur, bukan karena produkmu jelek, tapi karena first impression-nya gagal total. Tampilan website yang cantik itu bukan soal gaya-gayaan. Ini soal membangun kepercayaan, meningkatkan waktu kunjungan, dan yang paling penting: mendorong konversi. Orang yang merasa nyaman di sebuah halaman, kemungkinan besar akan lebih lama mengeksplor, membaca lebih lanjut, bahkan akhirnya klik tombol beli sekarang atau hubungi kami. Dan jangan salah, tampilan yang menarik bukan hanya urusan desainer aja. Frontend developer juga punya peran penting buat mewujudkan desain jadi pengalaman nyata. Gambar yang dimuat, cara loading-nya, responsif-nya, semuanya berpengaruh ke persepsi pengguna. Makanya, sebelum mikirin fitur canggih atau sistem login yang kompleks, pastikan dulu tampilannya enak dipandang. Karena di dunia online, mata adalah gerbang ke dompet 💸 Kita Akan Belajar Soal Gambar di Next.js Nah, masuk ke topik utama. Di artikel ini, kita bakal bahas pentingnya penggunaan image yang baik dalam framework Next.js. Terutama buat kamu yang masih baru banget mulai belajar Next.js, ini adalah salah satu fondasi penting yang sering dianggap remeh. Gambar itu bukan cuma pemanis tampilan, tapi juga bisa memengaruhi kecepatan website, SEO, dan kenyamanan pengguna. Salah satu keunggulan Next.js adalah dia punya sistem optimasi gambar bawaan yang canggih, dan yes, ini sangat direkomendasikan buat pemula karena bisa langsung dapet benefit besar tanpa ribet. Kita gak langsung masuk ke coding dulu ya. Di bagian awal ini, tujuannya supaya kamu ngerti dulu kenapa hal ini penting banget. Setelah kamu paham konteks dan alasannya, barulah nanti kita pelajari gimana cara implementasinya di proyek nyata. Jadi, siapkan diri kamu. Karena kita akan eksplor fitur image di Next.js yang bisa bikin website kamu bukan cuma tampil cantik, tapi juga cepat dan efisien 🚀 Gambar di Next.js Gak Sama Kayak <img>, Tapi Bisa Gantikan dengan Cara Lebih Pintar Kalau kamu udah biasa pakai tag HTML <img>, kamu mungkin bakal mikir: “Lah, kenapa sih harus repot-repot ganti ke komponen <Image /> dari Next.js?” Jawabannya simpel: <Image /> itu versi modern dan smart dari <img>. Dia bawa fitur bawaan seperti lazy loading otomatis, optimasi ukuran gambar, dan format gambar modern (seperti WebP) — semua ini bisa ngebantu banget buat performa website kamu tanpa perlu setting manual. Tapi perlu diingat ya, <Image /> ini bukan 100% drop-in replacement dari <img>. Ada aturan mainnya. Kalau kamu asal copy-paste <img> terus ganti jadi <Image>, kemungkinan besar error. Yuk kita lihat perbedaannya. Contoh <img> biasa: <img src="/assets/images/hero.png" alt="Hero Banner" /> Kalau kamu pakai komponen <Image /> dari Next.js, kamu harus import dulu dan struktur penggunaannya beda: import Image from 'next/image' export default function HeroSection() { return ( <div> <Image src="/assets/images/hero.png" alt="Hero Banner" width={600} height={400} /> </div> ) } Perlu diperhatikan, kamu gak bisa pakai path dari luar (URL full dari domain lain) secara langsung tanpa setting tambahan. Dan kamu gak boleh asal nulis <Image src="..." /> tanpa kasih tahu ukuran gambar. Jadi intinya: <Image /> itu memang gantinya <img>, tapi kamu harus ngerti cara pakainya supaya gak error dan justru bikin frustrasi. Harus Pakai width dan height, Kecuali Kamu Pakai Layout Khusus Satu hal yang bikin developer baru sering bingung waktu pakai <Image /> adalah: kenapa harus banget ada width dan height? Kalau pakai <img> kan tinggal tempel, langsung jalan. Tapi di Next.js, kamu bakal kena error kalau gak spesifik kasih ukuran. Ini bukan Next.js iseng, tapi justru demi kebaikan performa website kamu. Dengan ukuran yang jelas, browser bisa langsung hitung layout halaman sebelum gambar benar-benar muncul — hasilnya: gak ada layout shift yang ganggu UX. Contoh penggunaannya: import Image from 'next/image' export default function ProfileCard() { return ( <div className="w-[300px] p-4 border rounded-lg"> <Image src="/assets/images/avatar.png" alt="User Avatar" width={100} height={100} className="rounded-full" /> <h3 className="mt-4 font-semibold text-lg">Andi Prasetyo</h3> <p className="text-gray-500">Fullstack Developer</p> </div> ) } Tapi gimana kalau kamu gak tahu ukurannya, atau kamu pengen gambar ini responsif? Tenang, Next.js kasih opsi lain: kamu bisa pakai prop fill atau layout="fill" (di versi lama). Tapi ini perlu container yang punya posisi relatif dan ukuran yang ditentukan. Contoh penggunaan fill: import Image from 'next/image' export default function FullCover() { return ( <div className="relative w-full h-[400px]"> <Image src="/assets/images/banner.jpg" alt="Cover Image" fill style={{ objectFit: 'cover' }} /> </div> ) } Yang penting kamu ingat: kalau gak pakai width dan height, maka kamu harus pakai pendekatan khusus seperti fill, dan pastikan container-nya punya ukuran yang jelas. Kalau dua-duanya gak ada? Ya… bakal error. Jadi jangan lupa aturan main ini biar pengalaman coding kamu lebih lancar 😉 Semua Gambar Di-Load Secara Lazy Otomatis — Tanpa Perlu Kamu Atur Kalau kamu pernah ngoding manual dan pakai <img loading="lazy" /> biar gambar gak langsung dimuat semua pas user buka halaman, kamu pasti tahu pentingnya lazy loading. Ini bikin halaman lebih ringan dan cepat tampil, terutama kalau ada banyak gambar. Nah, enaknya pakai <Image /> dari Next.js itu kamu gak perlu nulis loading="lazy" lagi. Soalnya Next.js udah otomatis menerapkan lazy loading untuk semua gambar yang pakai komponen ini. Artinya, gambar hanya akan dimuat ketika sudah hampir masuk ke viewport (layar user). Contohnya kayak gini: import Image from 'next/image' export default function BlogThumbnail() { return ( <div className="p-6 border rounded-lg w-full max-w-md"> <Image src="/assets/images/article-cover.jpg" alt="Cover Artikel" width={600} height={350} /> <h2 className="text-xl font-bold mt-4">Tips Belajar Next.js untuk Pemula</h2> <p className="text-gray-600 mt-2">Pelajari cara kerja komponen image yang powerful ini...</p> </div> ) } Gak ada keterangan lazy loading di kode? Betul. Tapi saat kamu cek DevTools atau scroll pelan-pelan, kamu akan lihat bahwa gambar baru dimuat saat mendekati layar. Ini karena sistemnya udah smart banget. Jadi kamu tinggal fokus ke desain dan strukturnya aja, sisanya Next.js bantuin optimasinya dari belakang layar 🛠️ Format Gambar Diubah Otomatis ke WebP atau yang Lebih Ringan Pernah dengar format gambar WebP? Itu loh, format gambar yang lebih ringan dari JPEG atau PNG tapi kualitasnya tetap bagus. Format ini bisa bikin loading website lebih cepat — terutama di koneksi yang lambat atau di device mobile. Nah, satu lagi fitur keren dari <Image /> di Next.js adalah: kamu gak perlu konversi gambar ke WebP secara manual. Next.js secara otomatis akan memilih format gambar paling optimal berdasarkan browser yang digunakan user. Misalnya kamu upload gambar PNG, lalu user buka website dari Chrome yang support WebP — maka Next.js akan otomatis kasih versi WebP ke browser tersebut. Kalau browsernya gak support WebP, ya gak masalah — fallback ke format asli. Jadi aman buat semua pengguna. Coba lihat contoh ini: import Image from 'next/image' export default function ProductCard() { return ( <div className="border p-4 rounded-md w-[300px]"> <Image src="/assets/images/shoes.png" alt="Sepatu Keren" width={280} height={200} className="rounded-md" /> <h4 className="font-semibold text-lg mt-3">Sepatu Lari Pro</h4> <p className="text-sm text-gray-500 mt-1">Ringan, stylish, dan nyaman dipakai seharian</p> </div> ) } Kamu upload-nya shoes.png, tapi user bisa aja nerima shoes.webp karena Next.js bantuin konversi di server saat build atau runtime. Ini yang bikin kecepatan website kamu bisa meningkat tanpa perlu kamu convert manual satu per satu. Kesimpulannya: pakai <Image /> itu kayak punya asisten pribadi yang ngurusin performa gambar buat kamu, tinggal pakai dan santai 😎 Gambar Lokal Lebih Disukai Kalau Disimpan di Folder public/ Next.js punya cara tersendiri dalam menangani gambar. Secara default, dia lebih suka gambar-gambar yang kamu simpan di folder public/. Kenapa? Karena gambar yang disimpan di folder public/ itu bisa langsung diakses sebagai URL statis, tanpa perlu di-import atau dibundle ke dalam JavaScript. Contohnya gini, kamu punya gambar hero.jpg yang kamu simpan di: /public/assets/images/hero.jpg Nah, kamu tinggal panggil gambar itu dengan path /assets/images/hero.jpg di komponen <Image />. Contoh penggunaannya: import Image from 'next/image' export default function LandingHero() { return ( <div className="w-full h-[500px] relative"> <Image src="/assets/images/hero.jpg" alt="Gambar Hero Section" fill style={{ objectFit: 'cover' }} /> <div className="absolute bottom-10 left-10 text-white"> <h1 className="text-4xl font-bold">Bangun Bisnis Online dengan Website</h1> </div> </div> ) } Kamu gak perlu import gambar tersebut seperti file JS atau asset lainnya, cukup tulis path-nya dari folder public/. Oh ya, yang perlu diingat: path-nya gak boleh diawali dengan ./ atau ../, cukup langsung mulai dari root URL, misalnya /images/foto.jpg. Mau Pakai Gambar dari Luar? Bisa Banget, Tapi Harus Diatur Dulu Kalau kamu ambil gambar dari domain luar, misalnya dari CDN atau website lain (kayak https://images.unsplash.com/...), Next.js bakal blokir secara default. Ini demi alasan keamanan dan performa — supaya server gak sembarangan fetch gambar dari mana aja. Tapi tenang, kamu masih bisa pakai gambar eksternal asal kamu daftarin dulu domain-nya di file next.config.js. Contoh konfigurasinya: // next.config.js const nextConfig = { images: { remotePatterns: [ { protocol: 'https', hostname: 'images.unsplash.com', }, ], }, } module.exports = nextConfig Setelah itu, kamu bisa pakai gambar dari URL eksternal seperti ini: import Image from 'next/image' export default function ExternalImageExample() { return ( <div className="w-full max-w-[500px] mx-auto p-4"> <Image src="<https://images.unsplash.com/photo-1603791440384-56cd371ee9a7>" alt="Ilustrasi Startup" width={500} height={300} className="rounded-lg" /> <h3 className="mt-4 font-semibold text-lg">Inspirasi Kantor Startup</h3> </div> ) } Tanpa setting remotePatterns tadi, kode di atas bakal error pas build atau runtime. Jadi kalau kamu tahu bakal ambil gambar dari luar, pastikan domainnya udah dikonfigurasi dulu. Intinya, Next.js kasih kamu fleksibilitas — mau pakai gambar dari dalam public/ atau dari luar, semua bisa… asal kamu tahu cara mainnya 😄 Bikin Gambar Responsif? Bisa Banget Pakai sizes dan Tailwind CSS Salah satu fitur canggih di Next.js <Image /> adalah responsive image — yaitu gambar yang menyesuaikan ukurannya tergantung ukuran layar pengguna. Tapi… ini bukan cuma soal width: 100%. Next.js juga bantuin kita nge-load gambar yang ukurannya paling efisien buat ukuran layar tersebut. Supaya fitur ini bekerja maksimal, kamu bisa kombinasikan properti sizes di Next.js dengan Tailwind CSS. sizes itu kayak “petunjuk” ke browser, bilang: “kalau layarnya segini, ambil gambar ukuran sekian ya.” Contoh penggunaannya: import Image from 'next/image' export default function ResponsiveCard() { return ( <div className="w-full md:w-1/2 lg:w-1/3 p-4"> <Image src="/assets/images/responsive.jpg" alt="Gambar Responsif" width={800} height={600} sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw" className="rounded-xl w-full h-auto" /> <h3 className="text-lg font-bold mt-3">Desain Fleksibel untuk Semua Layar</h3> </div> ) } Dengan kode ini, saat layarnya kecil (mobile), gambar akan gunakan 100% lebar viewport, tapi kalau makin lebar, dia akan turun jadi 50% atau 33% tergantung ukuran layar. Browser akan pilih gambar ukuran terbaik, jadi gak boros bandwidth dan tetap tajam tampilannya. Pakai layout="fill" atau fill Kalau Kamu Mau Gambar Penuh dan Bebas Atur Pakai CSS Kadang kamu pengen gambar jadi background penuh dari sebuah elemen — contohnya kayak header, banner, atau hero section yang besar. Dalam kasus seperti ini, kamu bisa pakai properti fill (versi modern dari layout="fill" di Next.js versi lama). Tapi inget ya, harus dibungkus dengan elemen yang punya posisi relatif dan ukuran yang jelas. Tujuannya? Supaya Next.js tahu seberapa besar ruang yang harus diisi gambarnya. Contoh penggunaan: import Image from 'next/image' export default function FullWidthBanner() { return ( <div className="relative w-full h-[500px]"> <Image src="/assets/images/full-banner.jpg" alt="Banner Penuh" fill style={{ objectFit: 'cover' }} priority /> <div className="absolute bottom-10 left-10 text-white"> <h1 className="text-4xl font-bold">Selamat Datang di Website Kami</h1> <p className="mt-2 text-lg">Solusi modern untuk bisnis digital kamu</p> </div> </div> ) } Di atas, div wrapper punya className="relative w-full h-[500px]", dan itu penting banget supaya <Image fill /> bisa menyesuaikan 100% lebar dan tinggi elemen tersebut. Ditambah dengan objectFit: 'cover', hasilnya gambar akan memenuhi seluruh ruang dengan crop otomatis kalau perlu, mirip banget kayak background-image: cover. Jadi, kalau kamu butuh kontrol penuh pakai Tailwind atau CSS untuk bikin hero section yang estetik dan powerful, opsi fill ini wajib banget kamu kuasai 💪 Bantu SEO an Percepat LCP (Largest Contentful Paint) Salah satu alasan kenapa komponen <Image /> dari Next.js itu powerful banget adalah karena dia gak cuma bantu tampilan website jadi kece, tapi juga berdampak langsung ke performa — terutama dalam skor SEO dan Core Web Vitals. Salah satu metrik penting di sana adalah LCP (Largest Contentful Paint), yaitu waktu yang dibutuhkan sampai konten terbesar (biasanya gambar hero atau judul utama) muncul di layar. Kalau kamu pakai gambar biasa yang belum dioptimasi, bisa bikin loading lama. Tapi kalau pakai <Image />, gambar bakal: Di-lazy load kalau bukan bagian utamaDi-preload kalau jadi prioritasDi-resize ke ukuran paling efisien Semua itu bantu browser nampilin konten utama lebih cepat, yang akhirnya ningkatin skor SEO dan pengalaman pengguna. Contoh implementasi yang bagus buat LCP: import Image from 'next/image' export default function HeroSEO() { return ( <section className="relative w-full h-[600px]"> <Image src="/assets/images/lcp-hero.jpg" alt="Gambar Utama LCP" fill priority style={{ objectFit: 'cover' }} /> <div className="absolute bottom-10 left-10 text-white"> <h1 className="text-5xl font-bold">Bangun Masa Depan Digital Kamu</h1> <p className="mt-3 text-xl">Website cepat = peluang lebih besar ✨</p> </div> </section> ) } Dengan properti priority, gambar ini akan dimuat lebih awal, karena kita kasih tahu ke Next.js bahwa ini konten penting di halaman. Hasilnya? Waktu LCP jadi jauh lebih cepat, dan Google pun akan lebih senang saat ngecek performa websitemu lewat Lighthouse. Masih Bisa Lewatin Optimasi Kalau Emang Perlu Walaupun optimasi gambar itu sangat berguna, kadang ada kasus di mana kamu gak pengen Next.js optimize gambar yang kamu pakai. Misalnya: Kamu udah punya gambar yang sangat teroptimasi sendiri (WebP atau SVG)Kamu butuh kontrol penuh atas cara gambar di-loadKamu pengen pakai <img> biasa tanpa sistem dynamic image loader dari Next.js Caranya? Kamu tinggal set prop unoptimized di komponen <Image />. Ini akan kasih tahu Next.js: “udah, gak usah diapa-apain ya gambar ini.” Contoh penggunaannya: import Image from 'next/image' export default function CustomImageExample() { return ( <div className="w-full p-6"> <Image src="/assets/images/custom.svg" alt="Custom Gambar" width={300} height={300} unoptimized className="mx-auto" /> <p className="text-center mt-4 text-gray-600">Gambar ini tidak dioptimasi otomatis</p> </div> ) } Perlu dicatat, saat kamu pakai unoptimized, artinya kamu bertanggung jawab sendiri terhadap performa gambar tersebut. Ini bukan opsi default, dan hanya digunakan kalau kamu tahu betul apa yang kamu lakukan. Tapi enaknya, Next.js tetap fleksibel: kalau kamu butuh performa — dia bantuin. Kalau kamu butuh kontrol penuh — dia izinkan. Kombinasi yang mantap buat developer modern 💼 Penutup: Kamu Sudah Kuasai 10 Hal Penting Tentang Gambar di Next.js Kita udah belajar bareng 10 poin penting tentang penggunaan gambar di Next.js — mulai dari kenapa <Image /> lebih baik dari <img>, gimana caranya bikin gambar lebih responsif, cara kerja lazy loading otomatis, optimasi format seperti WebP, hingga konfigurasi untuk gambar eksternal. Bahkan, kita juga bahas bagaimana optimasi ini bisa bantu skor SEO dan performa LCP-mu jadi lebih baik. Intinya, <Image /> bukan cuma soal estetika. Dia adalah alat penting buat bikin website kamu lebih cepat, lebih ramah SEO, dan lebih nyaman diakses dari semua device. Tapi ini baru permulaan. Kalau kamu pengen next level dalam belajar Next.js — bukan cuma tahu dasar-dasarnya, tapi juga bisa bikin project yang siap masuk portofolio kerja remote — kamu bisa belajar langsung dari mentor expert di BuildWithAngga. Dengan belajar di BuildWithAngga, kamu bisa dapat banyak benefit seperti: ✅ Akses kelas selamanya ✅ Materi langsung dari praktisi industri ✅ Forum diskusi dan feedback bareng mentor ✅ Project real-world yang bisa jadi portofolio ✅ Peluang persiapan kerja remote, freelance, atau startup Jadi tunggu apa lagi? Yuk lanjutkan belajar bareng mentor di BuildWithAngga, karena masa depan digital dimulai dari langkah kecil hari ini 🚀