UI vs. UX: What’s the difference?

Tips Membuat Komponen Reusable di React

Dalam pengembangan aplikasi menggunakan React, komponen adalah fondasi utama untuk membangun antarmuka pengguna. Setiap bagian UI—mulai dari tombol, input, hingga layout kompleks—dibuat dalam bentuk komponen.

Namun, banyak developer pemula maupun menengah sering menghadapi masalah ketika komponen yang mereka buat terlalu spesifik, sulit dikelola, dan tidak bisa digunakan kembali di bagian lain aplikasi.

Inilah alasan mengapa komponen reusable menjadi sangat penting. Dengan membuat komponen yang dapat digunakan kembali, kita tidak hanya menghemat waktu pengembangan, tetapi juga menjaga konsistensi tampilan, mengurangi duplikasi kode, serta mempermudah pemeliharaan aplikasi dalam jangka panjang.

Artikel ini akan membahas prinsip, contoh implementasi, dan praktik terbaik dalam membuat komponen reusable di React, sehingga aplikasi yang kamu bangun menjadi lebih efisien, konsisten, dan scalable.

Contoh Kasus: Komponen yang Tidak Reusable

Misalnya, saat membuat tombol, banyak developer menuliskannya seperti ini:

function PrimaryButton() {
  return <button className="bg-blue-600 text-white px-4 py-2 rounded">Save</button>;
}

function SecondaryButton() {
  return <button className="bg-gray-200 text-black px-4 py-2 rounded">Cancel</button>;
}

Masalah dari pendekatan ini adalah:

  • Setiap variasi tombol membutuhkan file baru.
  • Tidak scalable saat jumlah variasi bertambah.
  • API tidak konsisten, sehingga membingungkan developer lain.

Solusinya: buat satu komponen Button yang fleksibel, dengan API sederhana seperti variant, size, dan isLoading.

1. Prinsip Penting dalam Reusable Component

Agar komponen benar-benar reusable, ada beberapa prinsip dasar yang perlu diikuti:

1.Single Responsibility
Komponen harus fokus pada satu tujuan. Misalnya, Button hanya mengatur tampilan tombol, bukan sekaligus validasi form.

2.API yang Konsisten dan Sederhana
Gunakan prop standar seperti variant, size, dan disabled. Hindari nama prop yang membingungkan atau terlalu panjang.

3.Composition Over Inheritance
Gunakan komposisi dengan children. Misalnya:

Pola ini lebih fleksibel daripada membuat banyak variasi Card.

4.Dukung Controlled & Uncontrolled
Komponen form sebaiknya mendukung dua mode: controlled (value) dan uncontrolled (defaultValue).

5.Aksesibilitas (A11y)
Gunakan atribut role dan aria-* agar komponen bisa digunakan oleh pembaca layar dan mendukung navigasi keyboard.

2. Studi Kasus: Membuat Button Reusable

Daripada membuat banyak jenis button, kita buat satu Button yang fleksibel.

Contoh Implementasi

import * as React from "react";

function cx(...classes: (string | undefined | false)[]) {
  return classes.filter(Boolean).join(" ");
}

type ButtonProps = {
  variant?: "primary" | "secondary" | "ghost";
  size?: "sm" | "md" | "lg";
  isLoading?: boolean;
  leftIcon?: React.ReactNode;
  rightIcon?: React.ReactNode;
  className?: string;
} & React.ButtonHTMLAttributes<HTMLButtonElement>;

export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
  ({ variant = "primary", size = "md", isLoading, leftIcon, rightIcon, className, children, ...props }, ref) => {
    const base = "inline-flex items-center justify-center rounded-xl font-medium focus:outline-none focus:ring transition";
    const sizes = {
      sm: "h-8 px-3 text-sm",
      md: "h-10 px-4 text-base",
      lg: "h-12 px-5 text-lg",
    }[size];

    const variants = {
      primary: "bg-black text-white hover:opacity-90",
      secondary: "bg-gray-200 text-black hover:bg-gray-300",
      ghost: "bg-transparent hover:bg-gray-100",
    }[variant];

    return (
      <button
        ref={ref}
        className={cx(base, sizes, variants, className)}
        disabled={isLoading || props.disabled}
        {...props}
      >
        {leftIcon && <span className="mr-2">{leftIcon}</span>}
        {isLoading ? "Loading…" : children}
        {rightIcon && <span className="ml-2">{rightIcon}</span>}
      </button>
    );
  }
);

Cara Menggunakan

<Button variant="primary" size="lg">Simpan</Button>
<Button variant="secondary" leftIcon={<Icon />}>Kembali</Button>
<Button variant="ghost" isLoading>Proses</Button>

Keuntungan:

  • API konsisten
  • Variasi mudah diatur dengan prop
  • Bisa dipakai di seluruh aplikasi

3. Studi Kasus: Card dengan Compound Components

Komponen Card biasanya punya header, content, dan footer. Jika digabung jadi satu, kurang fleksibel.

Contoh Implementasi

export function Card({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
  return <div className={`rounded-xl border shadow-sm ${className}`} {...props} />;
}

export function CardHeader({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
  return <div className={`p-4 border-b ${className}`} {...props} />;
}

export function CardContent({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
  return <div className={`p-4 ${className}`} {...props} />;
}

export function CardFooter({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
  return <div className={`p-4 border-t ${className}`} {...props} />;
}

Cara Menggunakan

<Card>
  <CardHeader>Judul</CardHeader>
  <CardContent>Ini konten card</CardContent>
  <CardFooter>Aksi tombol</CardFooter>
</Card>

Keuntungan: fleksibel, mudah dikustomisasi sesuai kebutuhan.

4. Controlled & Uncontrolled Input (TextField)

Input yang reusable sebaiknya mendukung dua mode penggunaan.

  • Controlled: value diatur dari state luar.
  • Uncontrolled: defaultValue digunakan tanpa state eksternal.

Hal ini membuat TextField lebih fleksibel di berbagai konteks form.

5. Compound Component Lanjutan (Tabs)

Untuk komponen kompleks seperti Tabs, gunakan React Context agar child bisa saling terhubung tanpa prop drilling. Dengan pola ini, developer bisa membuat tab yang fleksibel namun tetap konsisten.

6. Checklist Komponen Reusable

Sebelum digunakan luas, pastikan komponen kamu memenuhi kriteria berikut:

  • API kecil dan konsisten
  • Mendukung forwardRef
  • Bisa digunakan dalam mode controlled/uncontrolled (untuk input)
  • Aksesibilitas lengkap (role, aria-*, keyboard support)
  • Styling fleksibel (className atau style)
  • Ada dokumentasi dan testing

Kesimpulan

Membuat komponen reusable di React bukan hanya soal menulis kode sekali lalu digunakan berulang kali. Lebih dari itu, kita perlu merancang komponen dengan API yang jelas, fleksibel, konsisten, dan mendukung aksesibilitas.

Dengan pendekatan ini, kamu bisa membangun design system atau UI library internal yang lebih mudah dipelihara, konsisten secara tampilan, dan scalable seiring perkembangan aplikasi.