-
-
Notifications
You must be signed in to change notification settings - Fork 5
Fluid user experience
Writing applications for hypermedia (plain HTML over HTTP) brings a lot of benefits. URLs stay meaningful, page states stay easy to trace, and the browser and server can work together to provide unrivaled efficiency. The trade-off is that, left completely alone, these applications can sometimes feel clunky or old fashioned because every interaction is a full page load. But with a hypermedia, server-rendered application, it only takes a very small layer of client-side code to remove most of that roughness and make a hypermedia application feel fluid, while still keeping the underlying application simple and readable.
WebEngine can provide a fluid user experience without changing the basic shape of a web application. The page is still HTML, and the browser still requests URLs like normal. Forms still submit to the server, and links still point to real pages.
This is done by adding data-flux attributes within the HTML, and the Flux client-side micro framework will perform progressive enhancements to automate the client-side experience.
Full Flux documentation: https://www.php.gt/docs/Flux/
The main idea is simple: the server keeps returning complete HTML documents, and Flux swaps the matching parts of the current page with the matching parts from the new document.
The usual starting point is an ordinary form that already works without any JavaScript:
<form method="post">
<output data-flux="update">0</output>
<button name="do" value="increment" data-flux>Increment</button>
<button name="do" value="decrement" data-flux>Decrement</button>
</form>With no Flux, the form submits normally and the whole page reloads. With Flux loaded in the browser:
- clicking the button prevents the normal submit
- the form is submitted in the background
- the server returns the normal full HTML page
- Flux finds the matching
data-fluxupdate target in the returned document - only that part of the current page is replaced
That means we do not need to build a separate JSON API or a separate fragment-rendering system just to make one part of the page feel quicker.
The most useful update directives are:
-
data-flux="update"to replace the whole element -
data-flux="update-inner"to keep the element but replace its contents -
data-flux="update-link"to update an element only during Flux link navigation -
data-flux="update-link-inner"to keep the outer element but replace only its contents during Flux link navigation -
data-flux="update-attributes"to refresh just the element's attributes
Use update when the element itself may change:
<form method="post" data-flux="update">
<output>0</output>
<button name="do" value="increment" data-flux="submit">Increment</button>
</form>This is useful when the element's attributes or outer structure may change between responses.
Use update-inner when the wrapper should stay in place:
<section class="todo-list" data-flux="update-inner">
<ul>
<li>Write docs</li>
</ul>
</section>This is common when the outer element has layout or styling responsibilities and only the inner content changes.
Flux is not limited to one updated element. If the returned document contains several matching Flux targets, all of them can be refreshed from the same server response.
That is useful for pages such as:
- a pair of counters with a separate total
- a basket page with individual line items and an order summary
- a dashboard where one action changes several small widgets
The practical guide for this part of Flux is:
Flux can also enhance normal links:
<main data-flux="update-link">
<nav>
<a href="/reports" data-flux>Reports</a>
<a href="/settings" data-flux>Settings</a>
</nav>
</main>When a Flux link is clicked, Flux fetches the destination document in the background, updates the matching target, and updates the browser history.
This keeps navigation fluid while still treating each page as a real page with its own URL and its own server-rendered HTML.
See:
Sometimes the page needs to keep itself up to date without any user action. Flux supports that with live regions:
<time data-flux="live" data-flux-rate="5">12:00:00</time>This tells Flux to poll the current page every five seconds and replace the matching <time> element with the version from the newly returned document.
data-flux="live-inner" works in the same way, but keeps the outer element and replaces only its contents.
Live regions are useful for:
- clocks
- status badges
- queue counts
- notifications
- small dashboards
See:
Flux also supports background submission when a form changes:
<form method="post" data-flux="update-inner">
<input name="title" />
<button name="do" value="save" data-flux="autosave">Save</button>
</form>Here the button is hidden by Flux and used as the trigger for automatic saving whenever the form changes.
Attribute-only refreshes are useful when the content does not need changing, but the state does:
<button data-flux="update-attributes" disabled aria-busy="true">Save</button>This works well for:
disabledclassaria-*data-*
See:
Flux can rate-limit buttons, links, and live regions with data-flux-rate:
<button data-flux="submit" data-flux-rate="1">Save</button>This allows one request per second from that control. For live regions, the same attribute defines the polling interval.
That helps prevent accidental double-clicks or repeated rapid requests from a control. It does not replace server-side validation or idempotency checks.
See:
Flux also cooperates with your own JavaScript. When it replaces elements, it reinitialises new Flux elements and reattaches event listeners that were added through addEventListener.
That matters because without it, a DOM replacement would normally discard those listeners.
If you are debugging Flux itself or trying to understand a page's behaviour in detail, Flux also exposes a debug mode. The details are covered here:
Multi-pass rendering is the idea of giving the browser something useful first, then doing slower enhancement work afterwards.
For example, a page might:
- return a fully usable first response immediately
- let the browser render that page
- make a later request for slower-changing or heavier dynamic areas
- let Flux replace those areas once the second response arrives
This can improve perceived performance because the user sees something meaningful sooner, even though some work is still happening in the background.
The important part is that multi-pass rendering still fits the same WebEngine model:
- pages are still real URLs
- the server still returns HTML
- the browser is still updating from server-rendered documents
It is not a separate application architecture. It is just another way to stage the work.
This page is only the WebEngine-side overview. For the step-by-step Flux guide, continue with the Flux docs directly:
- https://www.php.gt/docs/Flux/Home/
- https://www.php.gt/docs/Flux/Your-first-Flux-form/
- https://www.php.gt/docs/Flux/Updating-part-of-the-page/
- https://www.php.gt/docs/Flux/Coordinating-multiple-updates/
- https://www.php.gt/docs/Flux/Link-navigation/
- https://www.php.gt/docs/Flux/Live-updates/
- https://www.php.gt/docs/Flux/Autosave-and-background-submits/
- https://www.php.gt/docs/Flux/Rate-limiting/
- https://www.php.gt/docs/Flux/List-of-flux-attributes/
Learn how to track speed and benchmarks in your applications.
- File-based routing
- Page views
- Page logic
- Dynamic URIs
- Headers and footers
- Custom HTML components
- Page partials
- Binding data to the DOM
- DOM manipulation
- Hello You tutorial
- Todo list tutorial
- Address book tutorial WIP
- Blueprints
- Application architecture
- Coding styleguide WIP
- PHP environment setup WIP
- Web servers WIP
- Background cron tasks
- Database setup WIP
- Client-side compilation WIP
- Testing WebEngine applications WIP
- Production checklist WIP
- Security WIP