Client-Side: Drag-and-Drop, Progress & Resumable
UX file upload yang profesional — dari drag-drop sampai lanjutkan upload yang terputus
Tujuan Pembelajaran
- Mampu membangun drag-and-drop file upload dengan preview
- Mampu mengimplementasikan progress bar dengan real-time feedback
- Mampu menangani resumable upload untuk koneksi tidak stabil
Analogi
Upload UX = Kirim Paket:
Upload biasa (tanpa UX):
User kirim paket → layar putih → tidak tahu status → anxiety
Upload baik (dengan UX):
- Drag-and-drop: user tinggal taruh paket di counter
- Preview: lihat isi paket sebelum kirim
- Progress: "Paket 45% terkirim"
- Resumable: kalau koneksi putus, lanjutkan dari 45%, tidak dari 0%
- Success: "Paket terkirim!" dengan preview hasil
Upload UX yang baik seperti kirim paket profesional — user tahu status, bisa preview, dan upload tidak gagal total kalau koneksi terputus.
Penjelasan Konsep
File upload UX sering diabaikan — padahal user experience yang buruk (tidak ada progress, gagal upload file besar) adalah sumber frustrasi utama.
Drag-and-Drop: Gunakan HTML5 Drag and Drop API atau library (react-dropzone, uppy). Events: dragover (highlight drop zone), dragleave, drop (ambil files). Tampilkan visual feedback: border berubah warna, animasi.
Preview: Sebelum upload, tampilkan preview. Untuk gambar: URL.createObjectURL(file) → <img src={previewUrl}>. Untuk video: sama dengan <video>. Untuk dokumen: tampilkan icon + filename + size. User bisa hapus file sebelum upload.
Progress: XMLHttpRequest memiliki upload.onprogress event (Fetch API belum support native). Alternatif: gunakan axios dengan onUploadProgress, atau gunakan tus (resumable upload protocol). Tampilkan: progress bar, percentage, speed (MB/s), estimated time remaining.
Resumable Upload: Untuk file besar + koneksi tidak stabil. Protokol: (1) Potong file menjadi chunks (1-5MB). (2) Upload chunks satu per satu dengan ID unik. (3) Server catat chunk yang sudah diterima. (4) Kalau upload terputus, client tanya server: “chunk mana yang sudah diterima?” → lanjut dari situ. Library: tus-js-client, uppy (dengan tus plugin).
Inti yang Perlu Dipahami
Bagian ini berfokus pada mampu membangun drag-and-drop file upload dengan preview., mampu mengimplementasikan progress bar dengan real-time feedback., dan mampu menangani resumable upload untuk koneksi tidak stabil. Jangan terburu-buru menghafal istilahnya. Lebih penting untuk memahami peran setiap konsep dan kapan konsep itu muncul dalam pekerjaan web development.
Saat membaca Client-Side, gunakan tujuan belajar sebagai penanda arah. Kalau kamu sudah bisa menjelaskan tujuan itu dengan kata-katamu sendiri, berarti fondasinya mulai terbentuk.
Cara Membayangkannya
Upload UX yang baik seperti kirim paket profesional — user tahu status, bisa preview, dan upload tidak gagal total kalau koneksi terputus. Analogi ini dipakai supaya konsep teknis tidak terasa melayang. Hubungkan setiap istilah dengan perannya: siapa yang meminta, siapa yang memproses, data apa yang berpindah, dan hasil apa yang diharapkan.
Kalau analoginya sudah terasa masuk akal, barulah lihat istilah teknisnya. Cara ini membuat materi lebih mudah dipahami daripada langsung menghafal definisi.
Saat Melihat Contoh Kode
Contoh kode pada chapter ini memakai bahasa tsx. Bacalah contoh kode sebagai ilustrasi alur, bukan sebagai bagian yang harus langsung dihafal.
Perhatikan nama fungsi, urutan langkah, dan data yang berpindah. Biasanya tiga hal itu sudah cukup untuk memahami hubungan antara teori dan praktik.
Konteks dalam Perjalanan Belajar
Setiap konsep di platform ini dipilih karena dipakai di industri. Fokus pada pemahaman, bukan hafalan.
Kamu sudah di bagian lanjutan. Mulai pikirkan bagaimana konsep ini dipakai di dunia kerja — bukan hanya untuk belajar, tapi untuk membangun produk nyata.
Gunakan pertanyaan reflektif dan prompt AI di akhir chapter sebagai latihan aktif. Membaca saja tidak cukup — kamu perlu menjelaskan ulang dengan kata-katamu sendiri.
Contoh Kode
// Drag-and-drop + progress dengan react-dropzone
import { useDropzone } from 'react-dropzone';
import { useState } from 'react';
function FileUploader() {
const [progress, setProgress] = useState(0);
const [preview, setPreview] = useState<string | null>(null);
const { getRootProps, getInputProps, isDragActive } = useDropzone({
accept: { 'image/*': [] },
maxSize: 10 * 1024 * 1024,
onDrop: async (files) => {
const file = files[0]!;
setPreview(URL.createObjectURL(file));
// Get presigned URL
const { url } = await fetch('/api/upload/presigned', {
method: 'POST',
body: JSON.stringify({ filename: file.name, contentType: file.type }),
}).then(r => r.json());
// Upload with progress via XMLHttpRequest
await new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.upload.onprogress = (e) => setProgress(Math.round((e.loaded / e.total) * 100));
xhr.onload = () => xhr.status === 200 ? resolve(null) : reject();
xhr.open('PUT', url);
xhr.setRequestHeader('Content-Type', file.type);
xhr.send(file);
});
},
});
return (
<div {...getRootProps()} className={`border-2 border-dashed p-8 ${isDragActive ? 'border-blue-500' : ''}`}>
<input {...getInputProps()} />
{preview && <img src={preview} alt="Preview" className="max-h-48" />}
{progress > 0 && <progress value={progress} max={100} />}
<p>{isDragActive ? 'Lepaskan file di sini' : 'Drag & drop atau klik untuk upload'}</p>
</div>
);
}Prompt AI
Bangun komponen file upload dengan: drag-drop zone, image preview, progress bar dengan XMLHttpRequest, dan validasi client-side (max size, allowed types). Minta AI review UX-nya.
Pertanyaan Reflektif
Coba upload file 500MB di aplikasi-mu. Apakah ada progress? Kalau koneksi putus di 80%, apakah kamu harus upload ulang dari 0%?