Pada aplikasi Laravel yang menggunakan Inertia.js, ada kebutuhan berulang untuk mengirim data yang sama ke banyak halaman. Contohnya adalah data user yang sedang login, nama aplikasi, atau notifikasi flash setelah proses create, update, dan delete. Jika setiap controller mengirim data itu secara manual, kode menjadi berulang, sulit dirawat, dan rawan inkonsistensi.

Di sinilah shared props berperan. Inertia menyediakan mekanisme untuk membagikan props global ke seluruh halaman melalui middleware, yang di Laravel umumnya diatur lewat HandleInertiaRequests. Pola ini sangat berguna, tetapi juga perlu digunakan dengan hati-hati. Jika semua data dibagikan secara global tanpa seleksi, performa dapat menurun dan risiko kebocoran data sensitif meningkat.

Artikel ini membahas cara kerja shared props di Laravel + Inertia.js, implementasi middleware yang rapi, pengiriman flash message success/error, cara mengakses props di Vue, serta praktik terbaik agar data global tetap aman dan efisien.

Mengapa Shared Props Dibutuhkan?

Dalam arsitektur Inertia, server merender respons berupa komponen halaman dan sekumpulan props. Props ini akan diterima oleh frontend, misalnya Vue. Secara default, setiap halaman menerima props yang dikirim dari controller atau route handler. Namun ada data yang hampir selalu dibutuhkan di banyak halaman, misalnya:

  • Informasi user yang sedang login
  • Nama aplikasi untuk navbar, title, atau footer
  • Flash message dari session
  • Preferensi UI sederhana seperti locale atau timezone

Tanpa shared props, Anda akan menulis hal serupa berulang kali di banyak controller. Dengan shared props, data tersebut cukup didefinisikan sekali di middleware, lalu tersedia di seluruh halaman Inertia.

Prinsip pentingnya: bagikan hanya data yang benar-benar sering dipakai secara global. Jangan menjadikan shared props sebagai tempat semua data aplikasi.

Peran Middleware HandleInertiaRequests

Laravel biasanya sudah menyediakan middleware HandleInertiaRequests saat Anda memasang stack Inertia. Middleware ini umumnya berada di:

app/Http/Middleware/HandleInertiaRequests.php

Middleware ini mewarisi kelas dasar Inertia dan memiliki method share() yang digunakan untuk mendefinisikan data global. Method tersebut akan digabung dengan shared data bawaan dari parent class.

Contoh implementasi shared props

<?php

namespace App\Http\Middleware;

use Illuminate\Http\Request;
use Inertia\Middleware;

class HandleInertiaRequests extends Middleware
{
    protected $rootView = 'app';

    public function share(Request $request): array
    {
        return array_merge(parent::share($request), [
            'app' => [
                'name' => config('app.name'),
            ],

            'auth' => [
                'user' => $request->user()
                    ? [
                        'id' => $request->user()->id,
                        'name' => $request->user()->name,
                        'email' => $request->user()->email,
                    ]
                    : null,
            ],

            'flash' => [
                'success' => fn () => $request->session()->get('success'),
                'error' => fn () => $request->session()->get('error'),
            ],
        ]);
    }
}

Struktur di atas cukup umum dan mudah dipakai di frontend:

  • app.name untuk konfigurasi global sederhana
  • auth.user untuk data user login
  • flash.success dan flash.error untuk notifikasi

Mengapa beberapa nilai dibungkus closure?

Pada contoh di atas, flash message dibungkus dengan closure:

'success' => fn () => $request->session()->get('success')

Pola ini berguna karena evaluasi nilainya dapat ditunda sampai benar-benar diperlukan oleh Inertia. Untuk data ringan efeknya mungkin kecil, tetapi untuk shared props yang bergantung pada proses lebih mahal, pendekatan lazy seperti ini membantu mengurangi overhead yang tidak perlu.

Shared Props untuk Auth User

Salah satu kebutuhan paling umum adalah menampilkan informasi user login pada layout, navbar, profile menu, atau halaman lain. Shared props membuat data ini tersedia secara konsisten.

Batasi field yang dibagikan

Kesalahan yang sering terjadi adalah langsung mengirim seluruh model user ke frontend:

'auth' => [
    'user' => $request->user(),
]

Walaupun terlihat praktis, ini berisiko. Model user bisa saja memiliki atribut yang tidak seharusnya tersebar ke semua halaman, misalnya:

  • role internal yang belum perlu ditampilkan
  • timestamp yang tidak relevan
  • flag atau metadata sensitif
  • relasi yang ikut termuat secara tidak sengaja

Praktik yang lebih aman adalah membuat payload eksplisit hanya dengan field yang memang dibutuhkan UI. Misalnya:

'auth' => [
    'user' => $request->user()
        ? [
            'id' => $request->user()->id,
            'name' => $request->user()->name,
            'email' => $request->user()->email,
        ]
        : null,
]

Jika aplikasi Anda memerlukan role atau permission di frontend, tetap pilih field minimal yang relevan. Jangan mengirim semua relasi permission jika yang dibutuhkan hanya boolean seperti can.manageUsers.

Kapan memakai shared auth user, kapan tidak?

Shared auth user cocok jika informasi itu dipakai hampir di semua halaman, misalnya untuk header atau menu. Namun jika ada halaman tertentu yang memerlukan profil lengkap, statistik akun, avatar, dan relasi tambahan, kirim data tersebut secara spesifik dari controller halaman itu. Dengan begitu payload global tetap kecil.

Konfigurasi Global Seperti App Name

Data konfigurasi sederhana juga cocok ditempatkan di shared props. Contoh paling umum adalah nama aplikasi:

'app' => [
    'name' => config('app.name'),
]

Keuntungannya adalah frontend tidak perlu hardcode nama aplikasi di banyak komponen. Jika nama aplikasi berubah, Anda cukup memperbarui konfigurasi Laravel. Pola ini juga berguna untuk:

  • environment label non-sensitif, misalnya “Staging”
  • locale aktif
  • timezone default untuk tampilan

Namun hindari membagikan konfigurasi internal yang tidak relevan bagi UI, apalagi yang bisa membuka detail infrastruktur atau keamanan.

Flash Message Success dan Error

Flash message adalah data singkat yang disimpan di session dan biasanya hanya hidup untuk request berikutnya. Dalam aplikasi Inertia, flash message umum dipakai setelah redirect, misalnya setelah berhasil menyimpan data.

Mengirim flash dari controller

Contoh setelah berhasil membuat data:

public function store(Request $request)
{
    $validated = $request->validate([
        'title' => ['required', 'string', 'max:255'],
    ]);

    // Simpan data...

    return redirect()
        ->route('posts.index')
        ->with('success', 'Post berhasil dibuat.');
}

Contoh saat ingin memberi pesan error umum:

public function destroy(Post $post)
{
    if ($post->is_locked) {
        return redirect()
            ->route('posts.index')
            ->with('error', 'Post yang terkunci tidak bisa dihapus.');
    }

    $post->delete();

    return redirect()
        ->route('posts.index')
        ->with('success', 'Post berhasil dihapus.');
}

Karena flash message sudah dibagikan lewat middleware, frontend tinggal membacanya dari props global.

Mengakses flash message di Vue

Pada Vue dengan Inertia, Anda bisa memakai usePage() untuk mengakses props:

<script setup>
import { computed } from 'vue'
import { usePage } from '@inertiajs/vue3'

const page = usePage()

const appName = computed(() => page.props.app.name)
const authUser = computed(() => page.props.auth.user)
const flashSuccess = computed(() => page.props.flash.success)
const flashError = computed(() => page.props.flash.error)
</script>

<template>
  <div>
    <header>
      <h2>{{ appName }}</h2>
      <p v-if="authUser">Login sebagai {{ authUser.name }}</p>
    </header>

    <div v-if="flashSuccess" class="alert alert-success">
      {{ flashSuccess }}
    </div>

    <div v-if="flashError" class="alert alert-danger">
      {{ flashError }}
    </div>
  </div>
</template>

Biasanya flash message ditampilkan di layout utama agar otomatis muncul di seluruh halaman setelah redirect. Ini lebih efisien dibanding menulis komponen notifikasi di setiap page component.

Data yang Sebaiknya Tidak Dibagikan Secara Global

Ini bagian yang paling penting dari sisi performa dan keamanan. Shared props akan ikut tersedia di banyak atau semua halaman Inertia. Artinya, setiap data yang Anda tempatkan di sana perlu diperlakukan sebagai data global sungguhan.

Jangan bagikan data besar

Hindari menaruh data berikut dalam shared props:

  • Daftar panjang kategori, produk, atau user
  • Hasil query dashboard yang kompleks
  • Relasi model yang besar
  • Data statistik yang hanya relevan di satu halaman

Semakin besar props global, semakin berat payload tiap respons Inertia. Ini berdampak pada waktu transfer, parsing di browser, dan kompleksitas debugging.

Jangan bagikan data sensitif

Hindari memasukkan hal-hal berikut ke shared props:

  • token API
  • secret key atau informasi environment sensitif
  • atribut internal user yang tidak perlu
  • permission mentah yang membuka struktur otorisasi internal tanpa alasan
  • data tenant atau organisasi yang tidak relevan di semua halaman

Perlu diingat, data yang dikirim ke frontend harus dianggap dapat dilihat oleh pengguna melalui DevTools. Walaupun tidak ditampilkan di UI, data tetap ada di payload respons.

Bagikan turunan data, bukan sumber mentahnya

Jika frontend hanya butuh informasi sederhana, kirim hasil olahan yang minimal. Contoh, daripada mengirim seluruh koleksi permissions, lebih baik kirim boolean yang jelas:

'auth' => [
    'can' => [
        'manage_users' => $request->user()?->can('manage users') ?? false,
    ],
]

Pendekatan ini lebih hemat payload dan lebih aman karena frontend hanya menerima informasi yang diperlukan untuk rendering UI.

Tips Struktur dan Praktik Terbaik

Gunakan namespace props yang konsisten

Kelompokkan shared props secara terstruktur, misalnya:

  • app.* untuk konfigurasi aplikasi
  • auth.* untuk user dan otorisasi sederhana
  • flash.* untuk notifikasi session

Struktur ini memudahkan tim memahami asal dan tujuan data.

Jangan campur kebutuhan layout dan kebutuhan halaman

Jika data dipakai hanya di satu halaman, kirim dari controller halaman itu. Shared props sebaiknya diisi hanya oleh data yang memang dipakai lintas halaman atau lintas layout.

Waspadai query tambahan di middleware

Method share() berjalan pada request Inertia. Jika Anda menambahkan query database berat di sana, biaya itu akan muncul berulang di banyak halaman. Jika memang perlu, pastikan query minimal, terindeks dengan baik, dan benar-benar dibutuhkan secara global.

Audit payload saat debugging

Jika halaman terasa lambat atau props terlalu besar, cek respons Inertia di browser DevTools. Perhatikan apakah ada data global yang terlalu besar, duplikatif, atau sensitif. Ini sering menjadi sumber masalah yang tidak terlihat dari sisi backend saja.

Pertimbangkan transformasi melalui resource atau DTO

Untuk aplikasi yang lebih besar, Anda dapat memisahkan transformasi data user ke class resource atau DTO agar bentuk data frontend lebih konsisten dan tidak bergantung langsung pada model Eloquent.

Kesalahan Umum yang Sering Terjadi

  • Mengirim seluruh object user tanpa filter field.
  • Menaruh query berat di shared props sehingga semua halaman ikut lambat.
  • Menggunakan shared props untuk data halaman spesifik yang seharusnya dikirim lokal dari controller.
  • Melupakan null handling ketika user belum login.
  • Menyimpan flash message di tempat yang salah sehingga notifikasi tidak muncul setelah redirect.

Jika flash message tidak tampil, periksa tiga hal: apakah controller benar-benar memakai with('success', ...) atau with('error', ...), apakah middleware share() membaca session dengan key yang benar, dan apakah komponen Vue mengakses path props yang sesuai.

Penutup

Shared props di Inertia.js Laravel adalah pola penting untuk membagikan data global dengan rapi dan konsisten. Middleware HandleInertiaRequests menjadi tempat yang tepat untuk menyimpan data seperti nama aplikasi, user login, dan flash message success/error. Dengan pendekatan ini, layout dan page component menjadi lebih sederhana karena tidak perlu terus-menerus menerima data global dari setiap controller.

Meski demikian, shared props harus digunakan secara disiplin. Bagikan hanya data yang benar-benar bersifat global, batasi field yang dikirim, hindari query berat, dan jangan pernah mengirim data sensitif hanya demi kemudahan akses di frontend. Jika aturan ini dijaga, integrasi Laravel dan Inertia akan tetap efisien, aman, dan mudah dipelihara seiring aplikasi bertambah besar.