web php

Progressive Enhancement And Server-Rendered UI

Progressive enhancement means the core page works with normal HTML and server-side PHP first. JavaScript then improves the experience where it is available.

This style is a strong fit for PHP because PHP is good at rendering HTML responses. Forms, links, redirects, validation errors, and flash messages can all work without requiring a client-side application.

Start with a working HTML flow

A normal server-rendered form should submit to a server route. The server validates, saves, redirects, and renders the next page.

PHP example
<?php

declare(strict_types=1);

$taskId = 42;

echo '<form method="post" action="/tasks/' . $taskId . '/complete">' . PHP_EOL;
echo '    <button type="submit">Mark complete</button>' . PHP_EOL;
echo '</form>' . PHP_EOL;

// Prints:
// <form method="post" action="/tasks/42/complete">
//     <button type="submit">Mark complete</button>
// </form>

That form is already usable. JavaScript can later intercept it to show a spinner, submit with fetch(), or update a row without a full page reload.

JavaScript enhances, the server remains responsible

Client-side code can improve interaction, but it must not be the only place validation or authorization happens. Users can disable JavaScript, requests can be forged, and clients can be modified.

PHP example
<?php

declare(strict_types=1);

function completeTask(int $taskId, bool $canEdit): string
{
    if (!$canEdit) {
        return 'Forbidden';
    }

    return 'Task ' . $taskId . ' completed';
}

echo completeTask(42, true) . PHP_EOL;

// Prints:
// Task 42 completed

The same server-side permission check is needed whether the request came from a normal form or JavaScript.

Design useful server responses

Progressive enhancement works best when server responses are meaningful.

For a normal form submission, a successful POST often redirects to another page. This is the Post/Redirect/Get pattern.

For a JavaScript request, the same route might return JSON if the request asks for it, or you might create a separate endpoint.

PHP example
<?php

declare(strict_types=1);

function responseForCompletedTask(bool $wantsJson): array
{
    if ($wantsJson) {
        return ['status' => 200, 'body' => '{"completed":true}'];
    }

    return ['status' => 303, 'headers' => ['Location' => '/tasks']];
}

$response = responseForCompletedTask(false);

echo $response['status'] . ' ' . $response['headers']['Location'] . PHP_EOL;

// Prints:
// 303 /tasks

Prefer real controls

Use links for navigation and forms/buttons for actions. That gives browsers, keyboards, screen readers, and search engines a meaningful base experience.

Do not make a <div> behave like a submit button when a real <button> would work. JavaScript can listen to the real button.

Useful enhancement targets

Good enhancement examples include:

  • confirmation prompts before destructive actions
  • inline validation messages while the server still validates on submit
  • optimistic UI updates that fall back to a normal redirect
  • sortable tables where the server can also sort via query strings
  • modal dialogs opened from links that still have useful destinations

What to check in a project

Check whether the feature still has a meaningful server-rendered path. Critical actions should not require JavaScript just to reach the server.

Check that server validation and authorization are complete. Client checks are user experience, not security.

Check that forms have labels, buttons, methods, and actions that make sense.

Check that JavaScript failure does not leave the user unable to complete the main task.

What you should be able to do

After this lesson, you should be able to explain progressive enhancement, build a server-rendered form flow, add JavaScript without replacing server responsibility, and choose HTML controls that work before enhancement.

Practice

Task: Enhance A Server Form

Design a small server-rendered form that works without JavaScript, then describe the JavaScript enhancement.

Requirements

  • Use declare(strict_types=1); if you include PHP code.
  • Render a real HTML form with method and action.
  • Include a submit button for a state-changing action.
  • Include the server-side validation or authorization rule in plain PHP.
  • Describe one JavaScript enhancement that improves the experience.
  • Explain what still works if JavaScript fails.

Check Your Work

Read the result and confirm the core action does not depend on JavaScript.

Show solution

This solution starts with a normal POST form. The enhancement is optional.

PHP example
<?php

declare(strict_types=1);

function renderCompleteTaskForm(int $taskId): string
{
    return '<form method="post" action="/tasks/' . $taskId . '/complete">'
        . '<button type="submit" data-enhance="confirm-complete">Mark complete</button>'
        . '</form>';
}

function completeTask(int $taskId, bool $canEdit): string
{
    if (!$canEdit) {
        return 'Forbidden';
    }

    return 'Task ' . $taskId . ' completed';
}

echo renderCompleteTaskForm(42) . PHP_EOL;
echo completeTask(42, true) . PHP_EOL;

// Prints:
// <form method="post" action="/tasks/42/complete"><button type="submit" data-enhance="confirm-complete">Mark complete</button></form>
// Task 42 completed

JavaScript enhancement: listen for clicks on [data-enhance="confirm-complete"] and show a confirmation dialog before submitting. If JavaScript fails, the form still submits to the server.

Why This Works

The HTML has a real method and action. The server still checks permission. JavaScript improves the interaction but does not become the only way to complete the task.