Tutorial React Dasar

Panduan lengkap mempelajari React dari nol untuk pemula

1. Pengenalan React

1

Apa itu React

React adalah library JavaScript untuk membangun antarmuka pengguna (UI) yang dikembangkan oleh Meta (Facebook).

Karakteristik React:

  • Library berbasis komponen — UI dipecah menjadi bagian-bagian kecil
  • Declarative — cukup deskripsikan tampilan, React yang urus perubahannya
  • Virtual DOM — perubahan UI sangat efisien dan cepat
  • One-way data flow — data mengalir dari parent ke child
  • Open source dan sangat populer (dipakai Facebook, Instagram, Airbnb)
  • Ekosistem besar dengan ribuan library pendukung
2

Mengapa Belajar React

  • Library UI paling populer di dunia saat ini
  • Permintaan kerja sangat tinggi di industri
  • Dasar untuk mempelajari Next.js, React Native, dll.
  • Komponen bisa dipakai ulang, hemat waktu development
  • Komunitas besar, dokumentasi lengkap
  • Mudah diintegrasikan dengan berbagai backend
  • Cocok untuk SPA (Single Page Application)
3

React vs Vanilla JavaScript

Perbandingan
VANILLA JAVASCRIPT:
// Update UI secara manual
const btn = document.getElementById('btn')
const counter = document.getElementById('counter')
let count = 0

btn.addEventListener('click', () => {
  count++
  counter.textContent = count  // manipulasi DOM manual
})

--- vs ---

REACT:
// React urus DOM-nya sendiri
function Counter() {
  const [count, setCount] = useState(0)

  return (
    <div>
      <p>{count}</p>
      <button onClick={() => setCount(count + 1)}>+</button>
    </div>
  )
}

KEUNTUNGAN REACT:
✓ Tidak perlu manipulasi DOM manual
✓ UI otomatis sinkron dengan data (state)
✓ Kode lebih terstruktur dan mudah dipelihara
✓ Komponen bisa dipakai ulang di mana saja

2. Requirements dan Setup

1

Requirements

  • Node.js versi 18 atau lebih baru
  • npm atau yarn sebagai package manager
  • Pemahaman dasar HTML, CSS, JavaScript
  • VS Code atau editor favorit lainnya
Cek versi Node.js
node --version
# Harusnya: v18.0.0 atau lebih baru

npm --version
# Harusnya: 8.x.x atau lebih baru
2

Membuat Proyek React Baru

Terminal
# Cara modern (direkomendasikan) — Vite
npm create vite@latest nama-proyek -- --template react

# Masuk ke folder
cd nama-proyek

# Install dependencies
npm install

# Jalankan development server
npm run dev
# Buka browser: http://localhost:5173

---

# Cara lama — Create React App (CRA)
npx create-react-app nama-proyek
cd nama-proyek
npm start
# Buka browser: http://localhost:3000
Vite jauh lebih cepat dari Create React App, disarankan untuk project baru
3

Struktur Folder Proyek (Vite)

Struktur Folder
nama-proyek/
├── public/             ← File statis (favicon, dll)
├── src/                ← Kode sumber utama
│   ├── assets/         ← Gambar, font, dll
│   ├── components/     ← Komponen React (buat sendiri)
│   ├── App.jsx         ← Komponen root utama
│   ├── App.css         ← Style untuk App
│   ├── main.jsx        ← Entry point aplikasi
│   └── index.css       ← CSS global
├── index.html          ← HTML template
├── package.json        ← Dependencies
└── vite.config.js      ← Konfigurasi Vite
4

File Pertama: main.jsx dan App.jsx

src/main.jsx — Entry point
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
import './index.css'

// Memasang komponen App ke dalam div#root di index.html
ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
)
src/App.jsx — Komponen utama
function App() {
  return (
    <div>
      <h1>Halo, React!</h1>
      <p>Aplikasi React pertama saya.</p>
    </div>
  )
}

export default App

3. JSX

1

Apa itu JSX

JSX (JavaScript XML) adalah sintaks yang memungkinkan kita menulis HTML di dalam JavaScript. React mengubah JSX menjadi JavaScript biasa di balik layar.

JSX Dasar
// JSX terlihat seperti HTML, tapi ini JavaScript
function App() {
  return (
    <div>
      <h1>Judul Halaman</h1>
      <p>Paragraf pertama saya.</p>
    </div>
  )
}

// Di balik layar, JSX diubah menjadi:
function App() {
  return React.createElement('div', null,
    React.createElement('h1', null, 'Judul Halaman'),
    React.createElement('p', null, 'Paragraf pertama saya.')
  )
}
// Makanya lebih enak pakai JSX!
2

Aturan Penting JSX

Aturan JSX
// 1. Harus ada satu elemen pembungkus (root element)
// SALAH:
return (
  <h1>Judul</h1>
  <p>Paragraf</p>
)

// BENAR: pakai div atau Fragment
return (
  <div>
    <h1>Judul</h1>
    <p>Paragraf</p>
  </div>
)

// BENAR: pakai Fragment (tidak tambah elemen ke DOM)
return (
  <>
    <h1>Judul</h1>
    <p>Paragraf</p>
  </>
)

// 2. class → className (karena 'class' kata kunci JS)
<div className="container">...</div>

// 3. Tag harus selalu ditutup
<input type="text" />   // BENAR
<br />                  // BENAR
<img src="foto.jpg" />  // BENAR

// 4. Ekspresi JavaScript pakai kurung kurawal {}
const nama = "Ahmad"
return <h1>Halo, {nama}!</h1>
return <p>1 + 1 = {1 + 1}</p>
return <p>{new Date().getFullYear()}</p>
3

Ekspresi dalam JSX

Ekspresi JSX
function Profil() {
  const nama = "Ahmad"
  const umur = 20
  const aktif = true
  const hobi = ["Coding", "Gaming", "Membaca"]

  return (
    <div>
      {/* Variabel */}
      <p>Nama: {nama}</p>
      <p>Umur: {umur} tahun</p>

      {/* Operasi */}
      <p>Lahir tahun: {2025 - umur}</p>

      {/* Conditional rendering */}
      <p>Status: {aktif ? "Aktif" : "Tidak Aktif"}</p>

      {/* Conditional show/hide */}
      {aktif && <span>🟢 Online</span>}

      {/* Render list */}
      <ul>
        {hobi.map((item, index) => (
          <li key={index}>{item}</li>
        ))}
      </ul>
    </div>
  )
}

4. Komponen

1

Membuat Komponen

Komponen adalah blok bangunan React. Setiap komponen adalah sebuah fungsi JavaScript yang mengembalikan JSX.

src/components/Greeting.jsx
// Aturan: nama komponen HARUS diawali huruf kapital
function Greeting() {
  return (
    <div>
      <h2>Selamat Datang!</h2>
      <p>Ini adalah komponen React pertama saya.</p>
    </div>
  )
}

export default Greeting

// Cara penggunaan di App.jsx:
import Greeting from './components/Greeting'

function App() {
  return (
    <div>
      <Greeting />  {/* Komponen dipanggil seperti tag HTML */}
      <Greeting />  {/* Bisa dipakai berulang kali! */}
    </div>
  )
}
2

Komponen dengan Style

Cara styling komponen
// 1. Inline style (pakai object, bukan string)
function Card() {
  const style = {
    background: 'white',
    padding: '20px',
    borderRadius: '8px',
    boxShadow: '0 2px 8px rgba(0,0,0,0.1)',
  }

  return <div style={style}>Isi kartu</div>
}

// 2. CSS file biasa
// Card.css:
// .card { background: white; padding: 20px; }

import './Card.css'

function Card() {
  return <div className="card">Isi kartu</div>
}

// 3. CSS Modules (class bersifat lokal)
import styles from './Card.module.css'

function Card() {
  return <div className={styles.card}>Isi kartu</div>
}
3

Komposisi Komponen

Contoh komposisi komponen
// Komponen kecil-kecil digabung jadi satu halaman
function Header() {
  return <header><h1>Logo Saya</h1></header>
}

function Sidebar() {
  return <aside><p>Menu navigasi</p></aside>
}

function Content() {
  return <main><p>Konten utama</p></main>
}

function Footer() {
  return <footer><p>© 2025</p></footer>
}

// Digabung di App
function App() {
  return (
    <div>
      <Header />
      <div style={{ display: 'flex' }}>
        <Sidebar />
        <Content />
      </div>
      <Footer />
    </div>
  )
}

5. Props

1

Pengenalan Props

Props (properties) adalah cara mengirim data dari komponen parent ke komponen child. Props bersifat read-only — child tidak boleh mengubah props yang diterima.

Props dasar
// Komponen menerima props sebagai parameter
function Greeting(props) {
  return <h1>Halo, {props.nama}!</h1>
}

// Lebih rapi dengan destructuring
function Greeting({ nama }) {
  return <h1>Halo, {nama}!</h1>
}

// Penggunaan — kirim data via atribut
function App() {
  return (
    <div>
      <Greeting nama="Ahmad" />
      <Greeting nama="Budi" />
      <Greeting nama="Citra" />
    </div>
  )
}
2

Berbagai Jenis Props

Jenis-jenis props
function ProfilKartu({ nama, umur, aktif, hobi, onClick }) {
  return (
    <div>
      <p>Nama: {nama}</p>         {/* String */}
      <p>Umur: {umur} tahun</p>  {/* Number */}
      <p>Aktif: {aktif ? 'Ya' : 'Tidak'}</p>  {/* Boolean */}
      <ul>
        {hobi.map((h, i) => <li key={i}>{h}</li>)}  {/* Array */}
      </ul>
      <button onClick={onClick}>Klik Saya</button>  {/* Function */}
    </div>
  )
}

// Penggunaan:
function App() {
  const handleKlik = () => alert('Kartu diklik!')

  return (
    <ProfilKartu
      nama="Ahmad"
      umur={20}
      aktif={true}
      hobi={["Coding", "Gaming"]}
      onClick={handleKlik}
    />
  )
}
3

Default Props dan Children

Default props & children
// Default props — nilai default jika tidak dikirim
function Tombol({ label = "Klik", warna = "black", onClick }) {
  return (
    <button
      onClick={onClick}
      style={{ background: warna, color: 'white', padding: '8px 16px' }}
    >
      {label}
    </button>
  )
}

<Tombol />                        {/* Klik, hitam */}
<Tombol label="Simpan" />         {/* Simpan, hitam */}
<Tombol label="Hapus" warna="red" /> {/* Hapus, merah */}

---

// Props children — konten yang disisipkan ke dalam komponen
function Kartu({ children }) {
  return (
    <div style={{ border: '1px solid #ccc', padding: '20px', borderRadius: '8px' }}>
      {children}
    </div>
  )
}

// Penggunaan:
<Kartu>
  <h2>Judul Kartu</h2>
  <p>Isi kartu bisa apa saja.</p>
</Kartu>

6. State dengan useState

1

Pengenalan State

State adalah data yang bisa berubah di dalam komponen. Saat state berubah, React otomatis me-render ulang komponen dengan tampilan terbaru.

useState dasar
import { useState } from 'react'

function Counter() {
  // useState(nilai awal) → [nilai saat ini, fungsi untuk update]
  const [count, setCount] = useState(0)

  return (
    <div>
      <p>Hitungan: {count}</p>
      <button onClick={() => setCount(count + 1)}>Tambah</button>
      <button onClick={() => setCount(count - 1)}>Kurang</button>
      <button onClick={() => setCount(0)}>Reset</button>
    </div>
  )
}

// ATURAN PENTING:
// Jangan ubah state langsung → count++ ← SALAH
// Selalu pakai fungsi setter → setCount(count + 1) ← BENAR
2

State dengan String dan Boolean

Berbagai tipe state
import { useState } from 'react'

function FormSederhana() {
  const [nama, setNama] = useState('')
  const [tampilkan, setTampilkan] = useState(false)

  return (
    <div>
      <input
        type="text"
        value={nama}
        onChange={(e) => setNama(e.target.value)}
        placeholder="Masukkan nama..."
      />

      <button onClick={() => setTampilkan(!tampilkan)}>
        {tampilkan ? 'Sembunyikan' : 'Tampilkan'}
      </button>

      {tampilkan && <p>Halo, {nama || 'Teman'}!</p>}
    </div>
  )
}
3

State dengan Object dan Array

State object & array
import { useState } from 'react'

function FormProfil() {
  // State berupa object
  const [profil, setProfil] = useState({ nama: '', email: '' })

  const handleChange = (e) => {
    setProfil({
      ...profil,                    // spread dulu nilai lama
      [e.target.name]: e.target.value  // baru update field-nya
    })
  }

  return (
    <form>
      <input name="nama" value={profil.nama} onChange={handleChange} placeholder="Nama" />
      <input name="email" value={profil.email} onChange={handleChange} placeholder="Email" />
      <p>Preview: {profil.nama} — {profil.email}</p>
    </form>
  )
}

---

function TodoList() {
  // State berupa array
  const [todos, setTodos] = useState(['Belajar React', 'Buat project'])
  const [input, setInput] = useState('')

  const tambah = () => {
    setTodos([...todos, input])  // spread array lama, tambah item baru
    setInput('')
  }

  const hapus = (index) => {
    setTodos(todos.filter((_, i) => i !== index))
  }

  return (
    <div>
      <input value={input} onChange={e => setInput(e.target.value)} />
      <button onClick={tambah}>Tambah</button>
      <ul>
        {todos.map((todo, i) => (
          <li key={i}>
            {todo} <button onClick={() => hapus(i)}>Hapus</button>
          </li>
        ))}
      </ul>
    </div>
  )
}

7. Hooks Penting

useState

Menyimpan dan mengubah data dalam komponen

useEffect

Menjalankan efek samping: fetch data, timer, subscribe

useRef

Akses elemen DOM langsung atau simpan nilai tanpa re-render

useContext

Berbagi data ke banyak komponen tanpa props drilling

1

useEffect

useEffect — efek samping
import { useState, useEffect } from 'react'

function DataPengguna() {
  const [users, setUsers] = useState([])
  const [loading, setLoading] = useState(true)

  // useEffect(fungsi, dependency array)
  useEffect(() => {
    // Jalankan saat komponen pertama kali muncul
    fetch('https://jsonplaceholder.typicode.com/users')
      .then(res => res.json())
      .then(data => {
        setUsers(data)
        setLoading(false)
      })
  }, [])  // [] → hanya jalankan sekali saat mount

  if (loading) return <p>Memuat data...</p>

  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>{user.name} — {user.email}</li>
      ))}
    </ul>
  )
}

// Variasi dependency array:
useEffect(() => { /* ... */ })          // Setiap re-render
useEffect(() => { /* ... */ }, [])     // Hanya sekali (mount)
useEffect(() => { /* ... */ }, [id])   // Setiap 'id' berubah

// Cleanup function (penting untuk timer, subscription)
useEffect(() => {
  const timer = setInterval(() => console.log('tick'), 1000)
  return () => clearInterval(timer)  // Bersihkan saat unmount
}, [])
2

useRef

useRef — referensi DOM
import { useRef } from 'react'

function FormFokus() {
  const inputRef = useRef(null)

  const handleKlik = () => {
    inputRef.current.focus()  // Fokus ke input secara langsung
  }

  return (
    <div>
      <input ref={inputRef} type="text" placeholder="Klik tombol di bawah..." />
      <button onClick={handleKlik}>Fokus ke Input</button>
    </div>
  )
}

---

// useRef untuk menyimpan nilai tanpa trigger re-render
import { useState, useRef, useEffect } from 'react'

function Timer() {
  const [detik, setDetik] = useState(0)
  const intervalRef = useRef(null)

  const mulai = () => {
    intervalRef.current = setInterval(() => {
      setDetik(prev => prev + 1)
    }, 1000)
  }

  const berhenti = () => {
    clearInterval(intervalRef.current)
  }

  return (
    <div>
      <p>{detik} detik</p>
      <button onClick={mulai}>Mulai</button>
      <button onClick={berhenti}>Berhenti</button>
    </div>
  )
}
3

useContext

useContext — berbagi data global
import { createContext, useContext, useState } from 'react'

// 1. Buat context
const TemaContext = createContext()

// 2. Buat provider (pembungkus)
function TemaProvider({ children }) {
  const [tema, setTema] = useState('terang')

  const toggleTema = () => {
    setTema(prev => prev === 'terang' ? 'gelap' : 'terang')
  }

  return (
    <TemaContext.Provider value={{ tema, toggleTema }}>
      {children}
    </TemaContext.Provider>
  )
}

// 3. Gunakan di komponen manapun
function TombolTema() {
  const { tema, toggleTema } = useContext(TemaContext)
  return (
    <button onClick={toggleTema}>
      Tema sekarang: {tema}
    </button>
  )
}

// 4. Bungkus App dengan Provider
function App() {
  return (
    <TemaProvider>
      <TombolTema />
    </TemaProvider>
  )
}
useContext cocok untuk data global seperti tema, bahasa, atau data user yang login

8. Event Handling

1

Event Dasar

Event handling
function KomponenEvent() {
  // Handler sebagai fungsi terpisah
  const handleKlik = () => {
    alert('Tombol diklik!')
  }

  const handleHover = () => {
    console.log('Mouse di atas elemen')
  }

  return (
    <div>
      <button onClick={handleKlik}>Klik Saya</button>
      <div onMouseEnter={handleHover}>Hover di sini</div>
    </div>
  )
}

// Event umum di React:
// onClick       → klik mouse
// onChange      → perubahan input
// onSubmit      → submit form
// onMouseEnter  → mouse masuk area
// onMouseLeave  → mouse keluar area
// onKeyDown     → tombol keyboard ditekan
// onFocus       → elemen mendapat fokus
// onBlur        → elemen kehilangan fokus
2

Event Object dan Form

Event object & form handling
import { useState } from 'react'

function FormLogin() {
  const [email, setEmail] = useState('')
  const [password, setPassword] = useState('')
  const [pesan, setPesan] = useState('')

  // e = event object
  const handleSubmit = (e) => {
    e.preventDefault()  // Cegah reload halaman

    if (!email || !password) {
      setPesan('Email dan password harus diisi!')
      return
    }

    setPesan(`Login dengan: ${email}`)
  }

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label>Email:</label>
        <input
          type="email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          placeholder="email@contoh.com"
        />
      </div>

      <div>
        <label>Password:</label>
        <input
          type="password"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
        />
      </div>

      <button type="submit">Login</button>

      {pesan && <p>{pesan}</p>}
    </form>
  )
}
3

Kirim Argumen ke Handler

Handler dengan argumen
function DaftarProduk() {
  const produk = [
    { id: 1, nama: 'Laptop', harga: 8000000 },
    { id: 2, nama: 'Mouse', harga: 150000 },
    { id: 3, nama: 'Keyboard', harga: 300000 },
  ]

  // Kirim argumen melalui arrow function
  const handleBeli = (id, nama) => {
    alert(`Membeli: ${nama} (ID: ${id})`)
  }

  const handleHapus = (id) => {
    console.log('Hapus produk ID:', id)
  }

  return (
    <ul>
      {produk.map(item => (
        <li key={item.id}>
          {item.nama} — Rp {item.harga.toLocaleString()}
          <button onClick={() => handleBeli(item.id, item.nama)}>
            Beli
          </button>
          <button onClick={() => handleHapus(item.id)}>
            Hapus
          </button>
        </li>
      ))}
    </ul>
  )
}

9. List dan Conditional Rendering

1

Render List dengan map()

Render list
function DaftarSiswa() {
  const siswa = [
    { id: 1, nama: 'Ahmad', nilai: 90 },
    { id: 2, nama: 'Budi', nilai: 85 },
    { id: 3, nama: 'Citra', nilai: 92 },
  ]

  return (
    <table>
      <thead>
        <tr><th>Nama</th><th>Nilai</th></tr>
      </thead>
      <tbody>
        {siswa.map(s => (
          // key WAJIB ada — harus unik di antara sibling
          <tr key={s.id}>
            <td>{s.nama}</td>
            <td>{s.nilai}</td>
          </tr>
        ))}
      </tbody>
    </table>
  )
}

// PENTING: Key harus unik dan stabil
// BENAR: key={item.id}      (pakai ID dari data)
// HINDARI: key={index}      (index bisa berubah saat list diurutkan/dihapus)
Selalu gunakan key yang unik dan stabil (seperti ID dari database), bukan index array
2

Conditional Rendering

Cara-cara conditional rendering
function StatusLogin({ isLogin, nama }) {
  // 1. if/else biasa
  if (!isLogin) {
    return <p>Silakan login terlebih dahulu.</p>
  }

  // 2. Ternary operator (inline)
  return (
    <div>
      {isLogin ? (
        <p>Selamat datang, {nama}!</p>
      ) : (
        <p>Kamu belum login.</p>
      )}

      {/* 3. Short-circuit && (tampilkan atau tidak sama sekali) */}
      {isLogin && <button>Logout</button>}
      {!isLogin && <button>Login</button>}
    </div>
  )
}

---

// Contoh nyata: loading state
function Artikel({ id }) {
  const [data, setData] = useState(null)
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState(null)

  useEffect(() => {
    fetch(`/api/artikel/${id}`)
      .then(r => r.json())
      .then(d => { setData(d); setLoading(false) })
      .catch(e => { setError(e.message); setLoading(false) })
  }, [id])

  if (loading) return <p>Memuat artikel...</p>
  if (error)   return <p>Error: {error}</p>
  if (!data)   return <p>Artikel tidak ditemukan.</p>

  return (
    <article>
      <h1>{data.judul}</h1>
      <p>{data.isi}</p>
    </article>
  )
}
3

Filter dan Sort List

Filter & sort list
import { useState } from 'react'

function DaftarProduk() {
  const [filter, setFilter] = useState('')
  const [urutkan, setUrutkan] = useState('nama')

  const produk = [
    { id: 1, nama: 'Laptop', kategori: 'Elektronik', harga: 8000000 },
    { id: 2, nama: 'Buku React', kategori: 'Buku', harga: 150000 },
    { id: 3, nama: 'Mouse', kategori: 'Elektronik', harga: 200000 },
    { id: 4, nama: 'Buku Python', kategori: 'Buku', harga: 120000 },
  ]

  const tampil = produk
    .filter(p => filter === '' || p.kategori === filter)
    .sort((a, b) => a[urutkan] > b[urutkan] ? 1 : -1)

  return (
    <div>
      <select onChange={e => setFilter(e.target.value)}>
        <option value="">Semua Kategori</option>
        <option value="Elektronik">Elektronik</option>
        <option value="Buku">Buku</option>
      </select>

      <select onChange={e => setUrutkan(e.target.value)}>
        <option value="nama">Urutkan: Nama</option>
        <option value="harga">Urutkan: Harga</option>
      </select>

      <ul>
        {tampil.map(p => (
          <li key={p.id}>{p.nama} — Rp {p.harga.toLocaleString()}</li>
        ))}
      </ul>
    </div>
  )
}

Kesimpulan dan Langkah Berikutnya

1

Ringkasan Materi

  • Pengenalan React dan perbedaannya dengan Vanilla JS
  • Setup proyek dengan Vite + React
  • JSX — sintaks HTML dalam JavaScript
  • Komponen — blok bangunan UI React
  • Props — mengirim data dari parent ke child
  • State dengan useState — data yang bisa berubah
  • Hooks penting: useEffect, useRef, useContext
  • Event handling dan form
  • Render list, key, dan conditional rendering
2

Langkah Selanjutnya

Topik lanjutan untuk memperdalam React:

  1. React Router — navigasi antar halaman di SPA
  2. useReducer — state management yang lebih kompleks
  3. Custom Hooks — membuat hook sendiri yang reusable
  4. useMemo & useCallback — optimasi performa
  5. React Query / SWR — data fetching yang lebih canggih
  6. Zustand / Redux — global state management
  7. TypeScript + React — type safety
  8. Testing — React Testing Library, Vitest
  9. Next.js — React framework untuk production
  10. React Native — mobile app dengan React
3

Tips Belajar

  • Buat mini project: counter, todo list, kalkulator
  • Pahami benar kapan pakai state vs props
  • Jangan takut error — baca pesan error dengan teliti
  • Gunakan React DevTools di browser untuk debug
  • Latihan memecah UI menjadi komponen kecil-kecil
  • Ikuti tutorial build project nyata di YouTube
  • Baca dokumentasi resmi React — sangat lengkap
  • Push semua project ke GitHub sebagai portofolio
4

Resources Berguna

Link Referensi
Dokumentasi Resmi React (sangat bagus!):
https://react.dev

React Tutorial Interaktif:
https://react.dev/learn

Vite (build tool):
https://vitejs.dev

React Router:
https://reactrouter.com

Tailwind CSS:
https://tailwindcss.com

Stack Overflow (Q&A):
https://stackoverflow.com (tag: reactjs)