DEV Community

Kamel
Kamel

Posted on

From Backend to Frontend: How I Merged Laravel with React Using Inertia.js

Ever wished your backend and frontend could just get along? I faced this head-on while building a dental clinic management system—and discovered a workflow that’s both powerful and refreshingly simple. Here’s how marrying Laravel and React with Inertia.js turned my app into a seamless, single-page powerhouse.

🏗️ The Blueprint: My Stack’s Secret Sauce

Forget clunky API calls and duplicated validation logic this architecture lets each layer shine:

  • Laravel (Backend): Handles the heavy lifting—routing, data, and validation.

  • React (Frontend): Crafts stunning, interactive user experiences.

  • Inertia.js (The Bridge): Magically connects them, letting you build SPAs without building an API.

🦷 Real-World Example: Managing Patients with Style

When developing a dental clinic management system, my top priorities were delivering an exceptional user experience and ensuring blazing-fast performance. After evaluating various options, Laravel emerged as the clear winner for the backend—its elegant syntax, robust ecosystem, and proven scalability made it the perfect foundation.

But I didn't stop there. Recognizing the need for a dynamic, responsive frontend that could match Laravel's capabilities, I leveraged my React expertise to create interactive user interfaces. The challenge? Bridging these two powerful technologies seamlessly.

Enter Inertia.js—the game-changing solution that eliminated the traditional API overhead while maintaining the strengths of both frameworks. This elegant bridge allowed me to:

  • Preserve Laravel's backend efficiency

  • Utilize React's component-based architecture

  • Maintain a single codebase without compromising on performance

  • Deliver smooth, app-like interactions

The result? A clinic management system that combines Laravel's backend prowess with React's frontend dynamism through Inertia's streamlined integration—proving that sometimes the best solutions come from combining the right technologies in innovative ways.

1. PatientController: Data, Ready for React
Here’s a slice of PHP that looks like it belongs in the future:

Image description

public function show(Patient $patient)
{
    return inertia('Patient/Show', [
        'patient' => new PatientResource($patient),
        'appointments' => $patient->appointments()->with(['treatments.payments'])->get(),
        'treatments' => $patient->treatmentRecords()->with(['payments'])->latest()->take(5)->get(),
    ]);
}
Enter fullscreen mode Exit fullscreen mode

Translation:

  • Fetches a patient and all their details (including nested relationships).

  • Returns everything straight to a React component—no manual API wrangling.

2.React Magic: Interactive Patient Details
And on the frontend? React takes over:

export default function Show({ auth, patient, appointments, treatments }) {
    const [expanded, setExpanded] = useState([]);
    const toggle = id => setExpanded(exp => exp.includes(id) ? exp.filter(i => i !== id) : [...exp, id]);

    return (
        <AuthenticatedLayout user={auth.user}>
            <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
                <DetailItem label="Contact Information" value={patient.contact_info} />
                <DetailItem label="Address" value={patient.address} />
                {/* More fields... */}
            </div>

            {/* Appointments Table */}
            {appointments.map(a => (
                <React.Fragment key={a.id}>
                    <tr onClick={() => toggle(a.id)}>
                        {/* Appointment details */}
                    </tr>
                    {expanded.includes(a.id) && (
                        <tr>
                            <td colSpan="5">{/* Nested treatments here */}</td>
                        </tr>
                    )}
                </React.Fragment>
            ))}
        </AuthenticatedLayout>
    );
}
Enter fullscreen mode Exit fullscreen mode

What’s cool:

  • Clicking an appointment row expands details—no reloads, instant UX.

📅 Real-Time Calendar: See Your Day at a Glance

Image description
Appointments load in real-time, grouped by date:

public function index()
{
    $appointments = Appointment::with('patient')
        ->whereMonth('date', now()->month)
        ->orderBy('date')
        ->orderBy('time')
        ->paginate(100);

    return Inertia::render('Appointments/Index', ['appointments' => $appointments]);
}
Enter fullscreen mode Exit fullscreen mode

And in React:

export default function Index({ auth, appointments }) {
    const [currentDate, setCurrentDate] = useState(new Date());
    const byDate = appointments.data.reduce((acc, a) => {
        const day = format(parseISO(a.date), 'yyyy-MM-dd');
        (acc[day] = acc[day] || []).push(a);
        return acc;
    }, {});

    return (
        <div className="grid grid-cols-7 gap-2">
            {daysInMonth.map(day => (
                <motion.div key={day.toString()} onClick={() => setCurrentDate(day)}>
                    {format(day, 'd')}
                    {byDate[format(day, 'yyyy-MM-dd')]?.map(a => (
                        <div key={a.id}>{a.patient.name}</div>
                    ))}
                </motion.div>
            ))}
        </div>
    );
}
Enter fullscreen mode Exit fullscreen mode

💡 Why This Approach Rocks

  • No API Hassle: Laravel passes data straight to React—no extra endpoints.

  • Best of Both Worlds: Use Laravel’s validation, ORM, and auth and React’s component magic.

  • Performance: Inertia can pre-render pages for lightning-fast loads.

  • Simple State: No Redux headaches—let Laravel handle sessions and shared data.

  • Reusable Components: Clean, snappy UI that’s easy to maintain.

📚 Lessons from the Trenches

  • Resource Formatting: Laravel’s API Resources (like PatientResource) keep data clean and predictable.

  • Component-First Thinking: Breaking big pages into bite-sized React pieces (like DetailItem) saves time and sanity.

  • Error Handling: Laravel’s validation errors flow straight into your React forms—no extra code.

  • State Management: For complex UIs, blend React’s state with Inertia’s shared data for the best results.

💬 Your Turn!

Have you tried the Laravel + React + Inertia combo? What worked, what didn’t, and what would you do differently?
Drop your experience below—let’s learn from each other!

Top comments (0)