Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Frujax Bundle Laravel Package

ruwork/frujax-bundle

The Ruwork Frujax Bundle integrates Frujax with Symfony to simplify AJAX-powered interactions in server-rendered applications. It helps handle asynchronous requests, update page fragments, and keep controllers and templates in control without full-page reloads.

View on GitHub
Deep Wiki
Context7

Getting Started

Frujax is a Symfony bundle for "friendly AJAX": the same route/render pipeline serves either a full page or a fragment, decided by a request marker, so forms and links keep working without JavaScript. It is not a Laravel package — it targets Symfony 3.x/4.0 (last release July 2018, repo archived). In a Laravel codebase you are porting the pattern, not installing the dependency.

Minimal steps to understand it:

  1. Open the bundle class and Resources/config/services.* first. The whole feature is usually three small services: a request matcher (decides "is this a frujax request?"), a kernel listener (injects state / adjusts the response), and a Twig extension (a helper like frujax_url() or a {% if frujax %} test).
  2. Find the request marker. Typically a header such as X-Frujax: 1, sometimes a _frujax route default. That marker is the contract your JavaScript must honor — nothing works unless the client sends it.
  3. First real use case: an HTML form that submits normally to a route. Without JS the server returns the full page; with JS you fetch() the same URL with the marker and swap the returned fragment into the DOM. One controller, one action, two renderings.

If you want this in Laravel today, composer require ruwork/frujax-bundle is the wrong move — copy the idea: a middleware reading a custom header plus a Blade layout that yields either @yield('content') or just the section.

Implementation Patterns

Opt-in per action, not global. Don't make every request frujax-aware. Mark the handful of endpoints (form submit, "load more", modal fetch) explicitly — via route defaults or a controller annotation — so the matcher stays cheap and predictable. A global listener that inspects every response is the classic source of "why did my redirect break" bugs.

Split by template, not by branching logic. The idiomatic pattern is: controller always renders the same logical template name; the bundle's Twig loader/listener appends a suffix (e.g. page.frujax.html.twig) when the marker is present, falling back to page.html.twig. This keeps fragments next to their full pages and avoids if ($request->isXmlHttpRequest()) { return $this->render(...partial...) } littering controllers.

Progressive enhancement for forms. The flow that pays off daily:

  1. Form posts to a normal route (non-JS path works, returns redirect + flash).
  2. JS intercepts submit, sends the same payload with the frujax header.
  3. Server returns the fragment; JS replaces the form container.
  4. On validation failure, the fragment is the re-rendered form with errors; on success, return a small "success" fragment or a frujax-redirect instruction the JS honors.

Redirect handling. POST/redirect/GET is the main friction point. Either the bundle's response listener converts a 302 into a JSON envelope ({"redirect": "..."}) or you special-case it. Decide early and standardize, because every form will hit this.

Compose with Symfony Form and Twig. The Twig extension is where day-to-day value lives: {{ frujax_url(path('order_submit')) }} builds the same URL with the marker, so templates never hardcode query strings. Add a global frujax boolean to the Twig context from the listener and gate chrome (nav, footer) on it in the base layout.

Laravel port sketch. Middleware sets view()->share('frujax', $request->header('X-Frujax') === '1'); a layout chooses full vs bare; a response macro converts redirects to JSON. Same three concerns, no bundle.

Gotchas and Tips

  • Archived and unmaintained. Last tag 2018-07-04, zero dependents, Symfony 4.1-era constraints. It will not install cleanly on current Symfony, and PHP 8 typed-property/Symfony\Contracts changes will bite. Read it as reference source, not a dependency.
  • Framework mismatch is the first gotcha. This is Symfony; Laravel has no AppKernel, no service tags, no bundle registration. Budget time to port, and don't expect the Twig loader tricks to map 1:1 onto Blade.
  • Cache poisoning via Vary. If a shared cache or CDN sits in front, the same URL returns different bodies for frujax vs normal requests. You must emit Vary: X-Frujax (or whatever the marker is), or users get fragments as full pages. This is the single most common production failure with this pattern.
  • Proxies strip custom headers. Load balancers and some corporate proxies drop unknown X-* headers. Detect the marker leniently (header OR query param) during rollout, then tighten.
  • X-Requested-With is not the same marker. Many bundles piggyback on X-Requested-With: XMLHttpRequest, which jQuery sets automatically but fetch() does not. If you mix axios/fetch/jQuery, be explicit about which header you send or half your requests will render full pages.
  • CSRF and flash messages. Fragments that re-render a form must include the CSRF token and consume flash messages exactly once; otherwise you get "token mismatch" on the second AJAX submit or duplicated flash toasts across partial renders.
  • Debugging checklist: hit the URL with curl -H 'X-Frujax: 1' and diff against a plain curl. If identical, the matcher isn't firing — check listener priority and that the bundle is registered above your app's response listeners.
  • Extension points to lean on: the request matcher (swap for a custom one to support a different header or an Accept: text/fragment negotiation) and the response listener (add your redirect-envelope logic there rather than in every controller). Those two are the seams worth customizing; everything else is thin glue.
  • Don't retrofit a whole app. The pattern shines on a few interactive islands (forms, pagination, modals). Converting every route to dual-render doubles your template surface and caching complexity for little gain.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons