Akses kelas selamanya

Ambil Promo
flash sale
hamburger-menu

Tips All

Meningkatkan skills menjadi 1% lebih baik

Reset
Kelas Tutorial Node JS 22: Bikin API Fitur Upload Photo dengan Busboy di BuildWithAngga

Tutorial Node JS 22: Bikin API Fitur Upload Photo dengan Busboy

Buat kamu yang ingin membangun website sewa rumah dari nol, memilih vanilla Node.js sebagai fondasi backend adalah keputusan yang tepat. Tanpa bantuan framework seperti Express, kamu jadi benar-benar paham cara kerja HTTP server, gimana request dan response diproses, dan bagaimana data bisa ditransfer lewat API. Ini penting banget terutama kalau kamu ingin bikin backend yang ringan, cepat, dan bisa disesuaikan penuh sesuai kebutuhan bisnis kamu. Selain itu, vanilla Node.js juga cocok banget dipakai untuk proyek yang skalanya masih kecil sampai menengah. Kamu punya kontrol penuh terhadap routing, middlewaare, hingga pengolahan file. Jadi kalau nanti butuh scaling atau ingin migrasi ke arsitektur yang lebih besar, kamu sudah punya fondasi yang kuat. Manfaat Utama Vanilla Node.js: Maintainable dan Scalable Salah satu keuntungan paling jelas ketika membangun backend dengan vanilla Node.js adalah kemudahan untuk membuat struktur yang maintainable. Kamu bebas bikin arsitektur yang modular, misalnya pisahin logic per folder seperti controller, service, middleware, hingga utilitas umum. Karena enggak bergantung pada library pihak ketiga, kamu juga enggak perlu terlalu khawatir dengan masalah compatibility antar versi atau update mendadak. Dari sisi scalability, Node.js dikenal sangat efisien dalam menangani banyak koneksi secara bersamaan karena sifatnya non-blocking dan event-driven. Bahkan tanpa Express, kamu tetap bisa membuat server yang siap menangani beban trafik tinggi, selama struktur kodenya dirancang dengan baik. Artinya, aplikasi kamu tetap jalan mulus walau pengguna bertambah banyak, apalagi kalau nanti didukung dengan deployment modern seperti Docker atau load balancer. Fokus Artikel Ini: Upload Foto Menggunakan Busboy + Postman Nah, di artikel ini kita bakal bahas salah satu fitur penting dalam proyek website sewa rumah: upload foto properti ke backend. Dalam dunia nyata, pemilik rumah pasti ingin tampilkan gambar rumah mereka supaya calon penyewa bisa lebih yakin sebelum booking. Karena itu, fitur upload foto bukan lagi tambahan, tapi sudah jadi fitur utama. Kita akan menggunakan Busboy, yaitu library kecil di Node.js yang sangat efektif buat parsing form-data, terutama saat user mengirimkan file lewat HTTP POST. File foto yang dikirim akan kita simpan langsung ke file system dalam folder khusus, tanpa perlu database. Dan sebagai pengujian, kita akan pakai Postman untuk mencoba kirim file dan lihat apakah file-nya berhasil disimpan. Dari proses ini, kamu bisa belajar bagaimana backend menerima file, menyimpannya, dan memberikan respons yang sesuai. Cara Install Node.js di Windows dan Membuat Proyek Pertama Backend API Sewa Rumah Langkah pertama yang perlu kamu lakukan adalah menginstall Node.js di Windows. Silakan buka situs resmi https://nodejs.org, lalu pilih versi LTS (Long Term Support) dan klik download. Setelah file selesai diunduh, jalankan installer dan ikuti langkah-langkah instalasinya seperti biasa hingga selesai. Setelah instalasi selesai, buka Command Prompt lalu ketik perintah berikut untuk memastikan bahwa Node.js dan npm sudah terpasang dengan benar: node -v npm -v Jika muncul versi dari Node.js dan npm, artinya kamu sudah siap membuat project backend pertamamu. Sekarang kita buat folder baru untuk proyek sewa rumah: mkdir sewa-rumah-api cd sewa-rumah-api Inisialisasi project Node.js dengan perintah: npm init -y Setelah itu, buat file utama server dengan nama index.js: // index.js const http = require('http'); const server = http.createServer((req, res) => { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ message: 'API Sewa Rumaah berjalan lancar' })); }); server.listen(3000, () => { console.log('Server berjalan di <http://localhost:3000>'); }); Lalu jalankan server dengan perintah berikut di terminal: node index.js Jika tidak ada error, maka akan muncul tulisan Server berjalan di http://localhost:3000. Itu artinya kamu sudah berhasil menjalankan server backend pertama kamu menggunakan vanilla Node.js, dan backend API sewa rumah siap kamu lanjutkan pengembangannya. Struktur Folder Proyek Backend Sewa Rumah (Vanilla Node.js) Untuk menjaga proyek tetap rapi, scalable, dan mudah dikembangkan, sangat disarankan membuat struktur folder yang jelas sejak awal. Walaupun kita tidak menggunakan framework seperti Express, bukan berarti kita menulis semua logic di satu file. Kita tetap bisa menyusun kode secara modular menggunakan pendekatan berikut. Contoh Struktur Folder sewa-rumah-api/ ├── controllers/ │ └── house.controller.js ├── data/ │ └── houses.json ├── middlewares/ │ └── upload.middleware.js ├── models/ │ └── house.model.js ├── routes/ │ └── house.routes.js ├── schemas/ │ └── house.schema.js ├── services/ │ └── house.service.js ├── uploads/ │ └── (folder kosong untuk menyimpan file foto) ├── utils/ │ └── generate-id.js ├── index.js ├── package.json Penjelasan Masing-Masing Folder controllers/ Berisi file yang menangani request dari user dan mengatur response-nya. Contohnya house.controller.js untuk handle upload foto, mengambil daftar rumah, atau menambahkan rumah baru. data/ Digunakan untuk menyimpan data sementara dalam format JSON sebelum kamu pakai database. Misalnya houses.json menyimpan daftar rumah yang bisa disewa. middlewares/ Berisi fungsi middleware yang dijalankan sebelum request masuk ke controller. Misalnya upload.middleware.js untuk menangani proses upload file dengan Busboy. models/ Digunakan untuk mendefinisikan struktur data seperti house.model.js. File ini bisa berisi constructor class atau struktur awal jika kamu pakai database nanti. routes/ Berisi logic pemetaan path URL ke fungsi controller secara manual. Contohnya house.routes.js mengatur endpoint /api/houses dan arahkan ke controller yang sesuai. schemas/ Berisi validasi data menggunakan library seperti Zod. Misalnya house.schema.js untuk validasi saat user menambahkan rumah baru agar inputnya sesuai aturan. services/ Berisi logic utama aplikasi seperti membaca file JSON, menyimpan data, atau filtering. house.service.js contohnya untuk mengambil semua rumah dari file JSON atau menambah data baru. uploads/ Folder kosong yang digunakan untuk menyimpan file hasil upload dari user. Misalnya foto rumah, yang diunggah melalui Postman akan masuk ke sini. utils/ Berisi fungsi bantu umum, misalnya generate-id.js untuk membuat ID unik setiap kali data baru ditambahkan. index.js File utama yang menjalankan HTTP server dan mengatur permintaan masuk, seperti menentukan rute dan memanggil middleware. Struktur seperti ini akan sangat membantu kamu saat proyek mulai bertambah kompleks. Setiap folder punya tanggung jawabnya sendiri, sehingga tim kamu pun bisa bekerja secara paralel tanpa mengganggu bagian lain. Cocok banget buat kamu yang mau bikin backend sederhana tapi profesional tanpa framework. Penjelasan File-Based Database Menggunakan .json untuk Backend API Sewa Rumah Dalam pengembangan backend API, terutagma saat proyek masih di tahap awal atau skala kecil, kita belum tentu langsung membutuhkan database besar seperti MySQL, MongoDB, atau PostgreSQL. Sebagai gantinya, kita bisa menggunakan pendekatan file-based database. Artinya, data akan disimpan dalam bentuk file .json di dalam folder proyek. File .json ini berfungsi mirip seperti tabel dalam database, isinya berupa array of objects yang merepresentasikan data — misalnya daftar rumah yang tersedia untuk disewa. Kelebihannya: setup sangat sederhana, tidak perlu install database server, dan bisa langsung digunakan dengan fs module dari Node.js. Metode ini cocok banget untuk belajar, prototyping, atau membangun MVP (Minimum Viable Product). Tapi perlu diingat, untuk kebutuhan produksi dengan banyak user dan skala besar, sebaiknya tetap gunakan database yang sesungguhnya. Cara Menggunakan File-Based Database di Proyek Sewa Rumah Langkah pertama, kamu buat file JSON di dalam folder data. Misalnya nama filenya houses.json dengan isi awal seperti ini: [] Isi array kosong karena nanti kita akan menambahkan rumah lewat endpoint POST. Lalu buat file service house.service.js di dalam folder services/ untuk mengelola proses baca dan tulis ke file tersebut. // services/house.service.js const fs = require('fs'); const path = require('path'); const { v4: uuidv4 } = require('uuid'); // opsional, bisa buat id unik kalau pakai library ini const filePath = path.join(__dirname, '../data/houses.json'); function readHouses() { const fileContent = fs.readFileSync(filePath, 'utf-8'); return JSON.parse(fileContent); } function saveHouses(houses) { fs.writeFileSync(filePath, JSON.stringify(houses, null, 2)); } function getAllHouses() { return readHouses(); } function addHouse(newHouse) { const houses = readHouses(); const houseWithId = { id: uuidv4(), // buat ID unik ...newHouse }; houses.push(houseWithId); saveHouses(houses); return houseWithId; } module.exports = { getAllHouses, addHouse }; Selanjutnya, buat controller untuk handle request dari user. Misalnya kita buat house.controller.js di folder controllers/. // controllers/house.controller.js const { getAllHouses, addHouse } = require('../services/house.service'); function handleGetHouses(req, res) { const houses = getAllHouses(); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(houses)); } function handleCreateHouse(req, res) { let body = ''; req.on('data', chunk => { body += chunk; }); req.on('end', () => { const data = JSON.parse(body); const newHouse = addHouse(data); res.writeHead(201, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(newHouse)); }); } module.exports = { handleGetHouses, handleCreateHouse }; Terakhir, sambungkan semuanya lewat index.js: // index.js const http = require('http'); const url = require('url'); const { handleGetHouses, handleCreateHouse } = require('./controllers/house.controller'); const server = http.createServer((req, res) => { const parsedUrl = url.parse(req.url, true); const method = req.method; const pathname = parsedUrl.pathname; if (method === 'GET' && pathname === '/api/houses') { return handleGetHouses(req, res); } if (method === 'POST' && pathname === '/api/houses') { return handleCreateHouse(req, res); } res.writeHead(404, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ message: 'Route not found' })); }); server.listen(3000, () => { console.log('Server berjalan di <http://localhost:3000>'); }); Kalau semuanya sudah kamu buat, kamu bisa uji coba endpoint ini di Postman. Lakukan GET untuk ambil semua rumah dan POST untuk menambah rumah baru. Data akan tersimpan otomatis di file houses.json. Dengan pendekatan ini, kamu bisa belajar alur lengkap backend API tanpa perlu mengatur database yang kompleks. Sederhaana tapi powerful buat latihan dan membangun proyek real yang kecil. Install dan Gunakan Busboy untuk Upload Foto Rumah di Backend API Sewa Rumah Setelah API backend kamu berhasil membaca dan menulis data ke file .json, sekarang kita akan menambahkan fitur baru: upload foto rumah. Dalam fitur ini, foto akan diunggah oleh user melalui Postman (atau frontend nantinya), lalu disimpan di folder uploads/, sedangkan file name dan path-nya akan disimpan di file .json sebagai referensi. Kita akan menggunakan Busboy, yaitu library ringan dan efisien untuk menangani multipart/form-data (khususnya saat user mengunggah file). Langkah 1: Install Busboy Buka terminal di folder proyek keamu, lalu jalankan perintah: npm install busboy Langkah 2: Buat Middleware Upload File Buat folder middlewares/ kalau belum ada. Di dalamnya, buat file baru bernama upload.middleware.js. // middlewares/upload.middleware.js const Busboy = require('busboy'); const fs = require('fs'); const path = require('path'); function handlePhotoUpload(req, res, callback) { const busboy = new Busboy({ headers: req.headers }); let fileName = ''; let uploaded = false; busboy.on('file', (fieldname, file, filename) => { const uploadPath = path.join(__dirname, '../uploads', filename); fileName = filename; uploaded = true; file.pipe(fs.createWriteStream(uploadPath)); file.on('end', () => { callback(null, fileName); }); }); busboy.on('finish', () => { if (!uploaded) { callback(new Error('No file uploaded')); } }); req.pipe(busboy); } module.exports = { handlePhotoUpload }; Langkah 3: Perbarui Service dan Controller Buka atau buat file house.service.js di dalam folder services/. Tambahkan logic untuk menyimpan nama file ke file JSON. // services/house.service.js const fs = require('fs'); const path = require('path'); const { v4: uuidv4 } = require('uuid'); const filePath = path.join(__dirname, '../data/houses.json'); function readHouses() { const data = fs.readFileSync(filePath, 'utf-8'); return JSON.parse(data); } function saveHouses(houses) { fs.writeFileSync(filePath, JSON.stringify(houses, null, 2)); } function addHouseWithPhoto(photoFileName) { const houses = readHouses(); const newHouse = { id: uuidv4(), title: 'Rumah Baru', photo: `/uploads/${photoFileName}` }; houses.push(newHouse); saveHouses(houses); return newHouse; } module.exports = { addHouseWithPhoto, readHouses }; Selanjutnya buka house.controller.js di folder controllers/ dan tambahkan logic untuk menangani upload foto. // controllers/house.controller.js const { addHouseWithPhoto, readHouses } = require('../services/house.service'); const { handlePhotoUpload } = require('../middlewares/upload.middleware'); function handleGetHouses(req, res) { const houses = readHouses(); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(houses)); } function handleUploadHousePhoto(req, res) { handlePhotoUpload(req, res, (err, filename) => { if (err) { res.writeHead(400, { 'Content-Type': 'application/json' }); return res.end(JSON.stringify({ message: err.message })); } const newHouse = addHouseWithPhoto(filename); res.writeHead(201, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ message: 'Rumah berhasil ditambahkan dengan foto', data: newHouse })); }); } module.exports = { handleGetHouses, handleUploadHousePhoto }; Langkah 4: Update index.js Buka file index.js di root proyek, lalu arahkan endpoint POST ke handler upload foto: // index.js const http = require('http'); const url = require('url'); const { handleGetHouses, handleUploadHousePhoto } = require('./controllers/house.controller'); const server = http.createServer((req, res) => { const parsedUrl = url.parse(req.url, true); const method = req.method; const pathname = parsedUrl.pathname; if (method === 'GET' && pathnamee === '/api/houses') { return handleGetHouses(req, res); } if (method === 'POST' && pathname === '/api/houses/upload-photo') { return handleUploadHousePhoto(req, res); } res.writeHead(404, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ message: 'Route not found' })); }); server.listen(3000, () => { console.log('Server berjalan di <http://localhost:3000>'); }); Cara Uji Coba API Upload Foto Rumah dengan Postman (Multipart Form Data) Setelah backend kamu selesai dibuat menggunakan Node.js dan Busboy, sekarang saatnya kita menguji apakah fitur upload foto rumah berjalan dengan benar. Untuk itu, kita akan menggunakan Postman, sebuah tool populer untuk testing API. Tujuan dari uji coba ini adalah mengirim file gambar (misalnya foto rumah) ke endpoint /api/houses/upload-photo, dan memastikan backend menyimpannya ke folder uploads/, serta mencatat informasi file tersebut di file houses.json. Persiapan Sebelum Testing Pastikan kamu sudah melakukan hal berikut: Jalankan server dengan perintah node index.jsFolder uploads/ sudah ada di root proyekFile houses.json sudah tersedia di dalam folder data/ dan berisi array kosong [] Langkah Uji Coba Upload Foto Rumah di Postman Buka aplikasi Postman dan ikuti langkah berikut: Pilih metode POST Masukkan URL endpoint kamu, contoh: <http://localhost:3000/api/houses/upload-photo> Klik tab Body → pilih opsi form-data Tambahkan form field dengan key: Key: photo Type: File Value: [pilih gambar dari komputermu] Pastikan field type kamu adalah File, bukan Text Klik tombol Send Hasil yang Diharapkan Jika semuanya berjalan dengan benar, kamu akan menerima response JSON seperti ini: { "message": "Rumah berhasil ditambahkan dengan foto", "data": { "id": "3e2237b0-2b2e-4d5f-9a8b-d6bd3e8e456c", "title": "Rumah Baru", "photo": "/uploads/foto-rumah.jpg" } } Cek folder uploads/ — harusnya ada file foto-rumah.jpg atau sesuai dengan file yang kamu upload. Buka file data/houses.json — datanya akan bertambah seperti ini: [ { "id": "3e2237b0-2b2e-4d5f-9a8wwwb-d6bd3e8e456c", "title": "Rumah aaBaru", "photo": "/uploads/foto-rumaaah.jpg" } ] Penutup dan Saran untuk Backend Developer Pemula Belajar membangun backend dari nol dengan vanilla Node.js adalah langkah awal yang luar biasa. Kamu jadi paham cara kerja HTTP server, bagaimana cara upload file tanpa bantuan framework, serta bagaimana menyimpan data di file .json secara manual. Ini adalah pondasi penting sebelum kamu melangkah ke tahap yang lebih kompleks seperti menggunakan Express, database relasional, autentikasi JWT, hingga deployment ke cloud. Tapi kadang, belajar sendiri bisa terasa lambat dan membingungkan. Karena itu, belajar langsung bersama mentor expert di BuildWithAngga bisa jadi jalan pintas terbaik buat kamu yang ingin mempercepat karier sebagai backend developer—khususnya yang ingin bekerja remote. Di BuildWithAngga, kamu bisa belajar hal teknis langsung dari para engineer berpengalaman, tidak hanya teori tapi juga praktik proyek nyata seperti: API backend untuk aplikasi sewa rumah, POS system, dan marketplaceValidasi data profesional dengan ZodUpload file dengan keamananeeeStruktur folder yang scalable untuk kerja timSimulasi kerja remote seperti di startup beneran Dan yang paling penting, kamu akan dibimbing untuk menyiapkan portofolio dan kemampuan teknis yang dibutuhkan perusahaan luar negeri. Jadi bukan sekadar belajar ngoding, tapi benar-benar diarahkan supaya siap kerja remote dan punya penghasilan yang lebih besar dari rumah. Kalau kamu serius ingin jadi backend developer yang siap kerja remote, jangan hanya belajar sendirian. Yuk, gabung belajar bareng mentor expert di BuildWithAngga. Siapkan masa depanmu mulai hari ini. 🚀

Kelas Tutorial Node JS Pemula Belajar Menerapkan File Based Database Projek Website Sewa Mobil di BuildWithAngga

Tutorial Node JS Pemula Belajar Menerapkan File Based Database Projek Website Sewa Mobil

Di era digital seperti sekarang, hampir semua jenis bisnis dituntut untuk hadir secara online. Mulai dari toko kecil sampai perusahaan besar, semua berlomba-lomba membangun sistem digital untuk menjangkau pelanggan lebih luas, meningkatkan efisiensi operasional, dan tentu saja menaikkan pendapatan. Misalnya gini, bayangin kamu punya bisnis penyewaan mobil. Kalau kamu hanya mengandalkan pelanggan yang datang langsung ke kantor atau tahu dari spanduk di pinggir jalan, jangkauannya terbatas banget. Tapi kalau kamu punya sistem online yang memungkinkan orang booking mobil lewat website dari rumah, potensi pasarnya langsung meluas ke mana-mana. Bahkan orang luar kota yang mau liburan pun bisa pesan lebih awal. Ini otomatis bikin potensi revenue perusahaan naik. Nah, untuk bikin sistem seperti itu, kita butuh teknologi backend yang kuat, stabil, dan bisa berkembang seiring pertumbuhan bisnis. Di sinilah Node.js masuk sebagai pilihan yang tepat. Node.js adalah lingkungan runtime JavaScript yang cepat, ringan, dan punya ekosistem library yang luas banget. Karena sifatnya non-blocking dan event-driven, Node.js cocok digunakan untuk aplikasi yang butuh performa tinggi dan skalabilitas, seperti sistem booking online atau dashboard manajemen armada mobil. Selain itu, Node.js juga gampang dipelajari, terutama buat kamu yang sudah familiar dengan JavaScript di frontend. Jadi proses belajarnya lebih cepat dan konsisten dari sisi bahasa pemrograman. Belajar Menerapkan File Based Database untuk Backend Sewa Mobil Pada artikel kali ini, kita akan bahas gimana cara bikin backend sederhana untuk katalog sewa mobil menggunakan pendekatan file based database. Konsep ini cocok banget buat pemula yang baru belajar backend dan belum siap menggunakan sistem database yang kompleks seperti MySQL atau MongoDB. File based database artinya data akan disimpan di dalam file—biasanya dalam format JSON—dan kita akan membaca, menulis, serta mengubah file tersebut menggunakan kode program. Meskipun tidak sekuat database sunguhan dalam hal kecepatan dan fleksibilitas, metode ini sangat pas digunakan untuk kebutuhan skala kecil atau saat kita sedang membangun prototype. Misalnya kita ingin bikin website sewa mobil yang menampilkan daftar mobil yang tersedia, lengkap dengan nama, merek, tahun produksi, dan harga sewa per hari. Semua informasi ini bisa disimpan dalam satu file .json dan dibaca oleh sistem Node.js saat dibutuhkan. Keuntungan pendekatan ini adalah sederhana, tidak perlu setup database server, dan kita bisa langsung fokus memahami alur backend, seperti membaca data, memfilter berdasarkan query, dan mengembalikan respons ke client dalam format JSON. Tapi tentu ada juga keterbatasannya, misalnya performa yang menurun kalau datanya makin besar, atau data tidak bisa diakses secara paralel secara efisien. Intinya, file based database adalah titik awal yang bagus buat belajar struktur backend sederhana dan memahami dasar-daesar bagaimana data bekerja di dalam aplikasi web. Dari sini, kamu bisa lanjut ke tahapan yang lebih kompleks, seperti menggunakan database relasional atau NoSQL di proyek-proyek selanjutnya. Di bagian berikutnya nanti, kita akan coba bangun struktur backendnya secara bertahap. Tapi yang pasti, kamu bakal dapet pengalaman langsung gimana cara kerja sistem backend dari nol. Cara Install Node.js Framework Terbaru di Windows Sebelum kita mulai bikin proyek backend, hal paling pertama yang perlu dilakukan tentu aja install Node.js dulu di komputermu. Kalau kamu pengguna Windows, tenang aja—caranya cukup gampang dan bisa kamu selesaikan dalam beberapa menit. Langkah pertama, buka browser dan pergi ke website resmi Node.js di alamat https://nodejs.org/. Di halaman utama kamu akan lihat dua tombol besar—satu untuk versi LTS (Long-Term Support) dan satu lagi versi Current. Disarankan kamu pilih versi LTS karena lebih stabil untuk proyek jangka panjang. Setelah file installer (.msi) selesai diunduh, jalankan file tersebut. Proses instalasi akan memandu kamu lewat beberapa langkah. Tinggal klik "Next" saja sampai selesai. Pastikan kamu mencentang opsi yang menambahkan Node.js ke dalam PATH, agar bisa digunakan langsung dari Command Prompt atau terminal. Setelah instalasi selesai, buka Command Prompt dan ketik perintah berikut untuk memastikan bahwa Node.js dan npm (Node Package Manager) sudah berhasil diinstall: node -v Perintah ini akan menampilkan versi Node.js yang terpasang, misalnya v22.0.0. npm -v Ini akan menampilkan versi npm, seperti 10.2.0. Kalau dua perintah ini menampilkan versi dengan benar, artinya kamu sudah siap memulai proyek Node.js-mu. Cara Membuat Proyek Backend Node.js untuk Katalog Sewa Mobil Setelah Node.js berhasil kamu install, sekarang kita mulai bikin proyek backend sederhana. Proyek ini akan menjadi dasar dari sistem katalog sewa mobil, di mana nanti kita bisa menampilkan daftar mobil, membaca data dari file, dan memberikan respons berupa JSON. Tapi di tahap ini, kita fokus dulu bikin struktur awal proyeknya. Pertama, buat dulu folder kosong untuk menyimpan semua file proyek kamu. Misalnya kamu bisa kasih nama katalog-sewa-mobil. Kamu bisa buat lewat File Explorer, atau langsung dari terminal: mkdir katalog-sewa-mobil cd katalog-sewa-mobil Setelah masuk ke folder proyek, jalankan perintah berikut untuk membuat file package.json. File ini akan menyimpan informasi dasar proyek seperti nama, versi, dan dependensi yang dibutuhkan. npm init -y Tanda -y artinya semua pertanyaan default akan dijawab otomatis, jadi prosesnya cepat. Selanjutnya, kita butauh satu file utama untuk menjalankan server. Biasanya kita kasih nama server.js, karena fungsinya untuk menjalankan HTTP server: echo. > server.js Kalau kamu pakai text editor seperti VS Code, kamu juga bisa langsung klik kanan dan pilih "New File", lalu tulis server.js. Setelah itu, kita akan buat juga satu folder bernama data. Di dalam folder ini nanti kita simpan file .json yang berfungsi sebagai database sederhana—alias file based database: mkdir data Sampai tahap ini, struktur folder kamu seharusnya terlihat seperti ini: katalog-sewa-mobil/ ├── data/ ├── package.json └── server.js Struktur ini adalah pondasi awal dari proyek backend kamu. Di artikel selanjutnya, kita akan mulai menulis logika untuk membaca file JSON dan menampilkan data mobil melalui endpoint yang bisa diakses lewat browser atau API client seperti Postman. Tapi sejauh ini, kamu udah siap banget buat lanjut ke tahap coding. Struktur Folder dan File yang Best Practice dalam Proyek Node.js Dalam membangun aplikasi backend Node.js yang rapi, scalable, dan mudah dipelihara, penting banget buat kita menggunakan struktur folder yang tertata dengan baik. Meskipun pada awalnya proyek kamu sederhana, membiasakan diri menggunakan struktur yang clean sejak awal akan memudahkan kamu atau tim kamu saat proyek berkembang lebih besar nanti. Berikut ini adalah struktur folder yang umum digunakan dalam proyek Node.js, khususnya kalau kamu tidak menggunakan framework seperti Express Generator, tapi ingin tetap rapi dan terorganisir: katalog-sewa-mobil/ ├── controllers/ ├── routes/ ├── services/ ├── models/ ├── data/ ├── utils/ ├── server.js ├── package.json Penjelasan tiap folder dan file: controllers/: berisi logika untuk menangani permintaan (request) dan mengirim respons (response). Di sinilah tempat kamu menulis logika endpoint API seperti /cars.routes/: berisi definisi endpoint dan rutenya, biasanya dipisahkan per fitur agar modular.services/: tempat logika bisnis utama, seperti filtering data, menyimpan data baru, validasi sederhana, dan lain-lain. Biasanya digunakan oleh controller.models/: menyimpan struktur data atau skema sederhana jika tidak menggunakan database seperti MongoDB atau Sequelize. Bisa juga sebagai lapisan abstraksi file JSON.data/: menyimpan file-file .json yang berperan sebagai file-based database.utils/: berisi helper function, seperti format tanggal, generate ID, logging, dll.server.js: titik masuk aplikasi, tempat menjalankan server dan menghubungkan semua bagian.package.json: konfigurasi dan dependensi proyek. Contoh Koding Folder Dasar server.js const http = require('http'); const { handleCarRoutes } = require('./routes/car.routes'); const PORT = 3000; const server = http.createServer((req, res) => { // Routing dasar if (req.url.startsWith('/cars')) { return handleCarRoutes(req, res); } res.writeHead(404, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ message: 'Route tidak ditemukan' })); }); server.listen(PORT, () => { console.log(`Server berjalan di <http://localhost>:${PORT}`); }); routes/car.routes.js const { getAllCars } = require('../controllers/car.controller'); function handleCarRoutes(req, res) { if (req.method === 'GET' && req.url === '/cars') { return getAllCars(req, res); } res.writeHead(404, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ message: 'Endpoint cars tidak ditemukan' })); } module.exports = { handleCarRoutes }; controllers/car.controller.js const { readCarData } = require('../services/car.service'); function getAllCars(req, res) { const cars = readCarData(); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(cars)); } module.exports = { getAllCars }; services/car.service.js const fs = require('fs'); const path = require('path'); function readCarData() { const filePath = path.join(__dirname, '../data/cars.json'); const fileContent = fs.readFileSync(filePath, 'utf8'); return JSON.parse(fileContent); } module.exports = { readCarData }; data/cars.json [ { "id": 1, "name": "Toyota Innova", "year": 2021, "price_per_day": 450000 }, { "id": 2, "name": "Suzuki Ertiga", "year": 2020, "price_per_day": 380000 } ] Dengan struktur ini, kamu bisa lebih mudah memisahkan tanggung jawab antar bagian. Misalnya, controller hanya fokus pada req dan res, sedangkan service bertanggung jawab untuk baca data atau menjalankan logika tertentu. Kalau nanti mau tambah fitur seperti tambah mobil baru, tinggal tambahkan method baru di car.service.js dan car.controller.js tanpa merusak struktur yang ada. Struktur ini juga memudahkan untuk testing, debug, dan scale up—baik ketika kamu mau ganti dari file based ke database sesungguhnya, atau saat mau deploy ke production. Cara Menyiapkan 100.000 Data JSON secara Otomatis Menyiapkan 100.000 data secara manual jelas bukan pilihan yang masuk akal. Untungnya, kita bisa menggunakan script Node.js sederhana untuk membuat data dummy dalam jumlah besar dan menyimpannya langsung ke file cars.json. Kita akan menggunakan module fs bawaan Node.js untuk menulis file, dan membuat array berisi objek-objek mobil dengan data yang dibangkitkan secara otomatis. Langkah pertama, buat file baru misalnya generate.js di dalam folder proyek kamu. Isi dengan kode berikut: const fs = require('fs'); const path = require('path'); // Fungsi bantu untuk menghasilkan nama mobil acak function generateCarName(index) { const brands = ['Toyota', 'Honda', 'Daihatsu', 'Suzuki', 'Nissan', 'Mitsubishi']; const models = ['Avanza', 'Brio', 'Xenia', 'Ertiga', 'Livina', 'Xpander']; const brand = brands[index % brands.length]; const model = models[index % models.length]; return `${brand} ${model}`; } const cars = []; for (let i = 1; i <= 100000; i++) { const car = { id: i, name: generateCarName(i), year: 2015 + (i % 10), // tahun antara 2015-2024 price_per_day: 250000 + (i % 10) * 10000 // variasi harga dari 250rb sampai 350rb }; cars.push(car); } const filePath = path.join(__dirname, 'data', 'cars.json'); fs.writeFile(filePath, JSON.stringify(cars, null, 2), (err) => { if (err) { console.error('Gagal menyimpan file:', err); } else { console.log('Berhasil membuat 100.000 data mobil ke dalam cars.json'); } }); Penjelasan singkat: Fungsi generateCarName() membuat nama mobil berdasarkan array merek dan model, lalu digabungkan.Loop for membuat 100.000 objek mobeil dengan id, name, year, dan price_per_day yang variatif.Semua data disimpan ke dalam array cars lalu ditulis ke file cars.json menggunakan fs.writeFile(). Untuk menjalankan scrippt ini, buka terminal di folder proyek lalu jalankan: node generate.js Dalam beberapa detik (tergantung spek komputermu), file cars.json di folder data/ akan terisi dengan 100.000 data mobil siap pakai. Kamu bisa pakai file ini untuk testing, simulasi API, atau keperluan performance benchmarking. Cara Menambahkan Route CRUD Data Mobil ke File JSON Setelah kita berhasil menyiapkan file cars.json, sekarang waktunya menambahkan fitur CRUD (Create, Read, Update, Delete) untuk memanipulasi data mobil yang tersimpan di file tersebut. Kita akan melanjutkan dari struktur yang sudah kita buat sebelumnya. Langkah 1: Update routes/car.routes.js untuk mendukung semua method HTTP const { getAllCars, getCarById, createCar, updateCarById, deleteCarById } = require('../controllers/car.controller'); function handleCarRoutes(req, res) { if (req.method === 'GET' && req.url === '/cars') { return getAllCars(req, res); } if (req.method === 'GET' && req.url.startsWith('/cars/')) { return getCarById(req, res); } if (req.method === 'POST' && req.url === '/cars') { return createCar(req, res); } if (req.method === 'PUT' && req.url.startsWith('/cars/')) { return updateCarById(req, res); } if (req.method === 'DELETE' && req.url.startsWith('/cars/')) { return deleteCarById(req, res); } res.writeHead(404, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ message: 'Endpoint cars tidak ditemukan' })); } module.exports = { handleCarRoutes }; Langkah 2: Update controllers/car.controller.js dengan semua operasi CRUD const { readCarData, writeCarData } = require('../services/car.service'); function getAllCars(req, res) { const cars = readCarData(); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(cars)); } function getCarById(req, res) { const id = parseInt(req.url.split('/')[2]); const cars = readCarData(); const car = cars.find(c => c.id === id); if (!car) { res.writeHead(404, { 'Content-Type': 'application/json' }); return res.end(JSON.stringify({ message: 'Mobil tidak ditemukan' })); } res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(car)); } function createCar(req, res) { let body = ''; req.on('data', chunk => { body += chunk; }); req.on('end', () => { const newCar = JSON.parse(body); const cars = readCarData(); newCar.id = cars.length > 0 ? cars[cars.length - 1].id + 1 : 1; cars.push(newCar); writeCarData(cars); res.writeHead(201, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(newCar)); }); } function updateCarById(req, res) { const id = parseInt(req.url.split('/')[2]); let body = ''; req.on('data', chunk => { body += chunk; }); req.on('end', () => { const updatedData = JSON.parse(body); const cars = readCarData(); const index = cars.findIndex(c => c.id === id); if (index === -1) { res.writeHead(404, { 'Content-Type': 'application/json' }); return res.end(JSON.stringify({ message: 'Mobil tidak ditemukan' })); } cars[index] = { ...cars[index], ...updatedData }; writeCarData(cars); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(cars[index])); }); } function deleteCarById(req, res) { const id = parseInt(req.url.split('/')[2]); let cars = readCarData(); const index = cars.findIndex(c => c.id === id); if (index === -1) { res.writeHead(404, { 'Content-Type': 'application/json' }); return res.end(JSON.stringify({ message: 'Mobil tidak ditemukan' })); } const deleted = cars.splice(index, 1)[0]; writeCarData(cars); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(deleted)); } module.exports = { getAllCars, getCarById, createCar, updateCarById, deleteCarById }; Langkah 3: Tambahkan helper writeCarData di services/car.service.js const fs = require('fs'); const path = require('path'); function readCarData() { const filePath = path.join(__dirname, '../data/cars.json'); const fileContent = fs.readFileSync(filePath, 'utf8'); return JSON.parse(fileContent); } function writeCarData(data) { const filePath = path.join(__dirname, '../data/cars.json'); fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf8'); } module.exports = { readCarData, writeCarData }; Sampai tahap ini, kamu sudah memiliki CRUD lengkap: GET /cars untuk mendapatkan semua mobilGET /cars/:id untuk mendapatkan mobil berdasarkan IDPOST /cars untuk menambahkan mobil baruPUT /cars/:id untuk mengedit data mobilDELETE /cars/:id untuk menghapus mobil Kamu bisa menguji semua endpoint ini lewat Postman atau Insomnia. Pastikan kamu menyetel header Content-Type: application/json untuk request POST dan PUT. Cara Melakukan Uji Coba API Backend Node.js Menggunakan Postman Setelah kamu menyelesaikan pembuatan backend katalog sewa mobil berbasis file JSON, langkah selanjutnya adalah mengujinya menggunakan Postman. Postman adalah aplikasi yang sangat membantu untuk mencoba dan mengevaluasi API secara manual. Kamu bisa kirim request, lihat respons, dan memastikan setiap endpoint bekerja seperti yang diharapkan. Sebelum membuka Postman, pastikan server kamu sedang berjalan dengan menjalankan: node server.js Setelah itu, buka aplikasi Postman dan ikuti cara berikut untuk mencoba tiap endpoint: Menguji GET semua mobil Pilih metode GETMasukkan URL http://localhost:3000/carsKlik toambol SendRespons yang keluar harus berupa array mobil seperti: [ { "id": 1, "name": "Toyota Avanza", "year": 2020, "price_per_day": 350000 }, { "id": 2, "name": "Honda Brio", "year": 2021, "price_per_day": 300000 } ] Menguji GET mobil berdasarkan ID Pilih metode GETMasukkan URL http://localhost:3000/cars/1Klik SendRespons yang keluar: { "id": 1, "name": "Toyota Avanza", "year": 2020, "price_per_day": 350000 } Menguji POST untuk menambahkan mobil baru Pilih metode POSTMasukkan URL http://localhost:3000/carsKlik tab Body → pilih raw → pilih JSONIsi konten seperti ini: { "name": "Nissan Serena", "year": 2022, "price_per_day": 400000 } Klik SendRespons akan menampilkan data mobil baru yang sudah diberi ID: { "name": "Nissan Serena", "year": 2022, "price_per_day": 400000, "id": 100001 } Menguji PUT untuk mengupdate mobil Pilih metode PUTMasukkan URL http://localhost:3000/cars/1Klik tab Body → pilih raw → pilih JSONIsi konten: { "price_per_day": 375000 } Klik SendRespons: { "id": 1, "name": "Toyota Avanza", "year": 2020, "price_per_day": 375000 } Menguji DELETE untuk menghapus mobil Pilih metode DELETEMasukkan URL http://localhost:3000/cars/1Klik SendRespons: { "id": 1, "name": "Toyota Avanza", "year": 2020, "price_per_day": 375000 } Setelah kamu coba semua endpoint di atas dan hasilnya sesuai harapan, artinya sistem backend kamu sudah bekerja dengan baik. Pastikan untuk melakukan testing secara berkala saat menambahkan fitur baru agar tidak ada yang rusak. Postman juga memungkinkan kamu menyimpan request untuk digunakan ulang, jadi sangat berguna saat pengembangan jangka panjang. Penutup dan Saran untuk Backend Developer Pemula yang Ingin Kerja Remote Belajar membuat backend dari nol memang nggak instan. Tapi dengan konsistensi dan struktur belajar yang benar, kamu bisa banget jadi backend developer yang siap kerja secara remote, bahkan dari rumah sendiri. Project sederhana seperti sistem katalog sewa mobil berbasis file JSON ini adalah titik awal yang bagus banget. Kamu bisa belajar banyak hal mulai dari routing, struktur folder, cara kerja server, sampai CRUD API—semuanya real dan langsung dipraktikkan. Kalau kamu merasa belajar sendiri bikin bingung dan butuh arahan yang jelas, jangan khawatir. Di BuildWithAngga, kamu bisa belajar langsung bareng mentor expert yang sudah berpengalaman bertahun-tahun di dunia backend dan kerja remote. Kursus yang kami siapkan nggak cuma teori doang, tapi berbasis proyek nyata, dengan penjelasan yang mudah dipahami dan step-by-step. Benefit belajar bareng mentor di BuildWithAngga antara lain: Belajar dari mentor yang pernah kerja di startup besar dan luar negeriMateri project-based, bukan teori kosongSertifikat resmi yang bisa kamu pakai untuk melamar kerja remoteAkses ke komunitas belajar, tanya jawab, dan sharing job opportunityUpdate materi sesuai tren industri backend terbaruaaaAda latihan interview & review portofolio biar kamu lebih siap ke dunia kerja Dunia kerja remote itu real, dan terbuka lebar buat siapa aja yang siap. Jadi daripada cuma jadi penonton, mending kamu ikut mulai dari sekarang. Bangun skill-mu, buat portofolio backend-mu, dan persiapkan diri untuk masuk ke dunia kerja global bareng BuildWithAngga. 🚀

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.