HtmxRequest Showcase
See what htmx sends in request headers. Click buttons on the left to update the table on the right.
See what htmx sends in request headers. Click buttons on the left to update the table on the right.
Different buttons set different trigger and target values
Use hx-prompt to get user input - value appears in prompt property
currentUrl shows browser URL when request was made
Click "Change URL", then click other buttons - currentUrl will show new URL
Click button to push URL, then browser back - isForHistoryRestoration = true
hx-history="false" disables htmx cache, forcing server request on back
Compare hx-boost link vs regular button - see isBoosted difference
Values from request headers - updates in real-time
| Property | Value |
|---|---|
| isHtmx | false |
| isBoosted | false |
| currentUrl | (null) |
| target | (null) |
| trigger | (null) |
| triggerName | (null) |
| prompt | (null) |
| isForHistoryRestoration | false |
<?php
declare(strict_types=1);
namespace App\Controller;
use Mdxpl\HtmxBundle\Attribute\HtmxOnly;
use Mdxpl\HtmxBundle\Request\HtmxRequest;
use Mdxpl\HtmxBundle\Response\HtmxResponse;
use Mdxpl\HtmxBundle\Response\HtmxResponseBuilder;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Attribute\Route;
#[Route('/request-showcase')]
final class RequestShowcaseController extends AbstractController
{
#[Route('', name: 'app_request_showcase')]
public function index(HtmxRequest $htmx): HtmxResponse
{
return HtmxResponseBuilder::create($htmx->isHtmx)
->success()
->view('request_showcase.html.twig', [
'htmx' => $htmx,
])
->build();
}
/**
* Endpoint for testing HtmxRequest properties
*/
#[Route('/request', name: 'app_request_showcase_request')]
#[HtmxOnly]
public function request(HtmxRequest $htmx): HtmxResponse
{
return HtmxResponseBuilder::create($htmx->isHtmx)
->success()
->viewBlock('request_showcase.html.twig', 'actionResult', ['htmx' => $htmx])
->viewBlock('request_showcase.html.twig', 'requestInfoOob', ['htmx' => $htmx])
->build();
}
/**
* Endpoint that triggers a prompt dialog - demonstrates prompt property
*/
#[Route('/prompt', name: 'app_request_showcase_prompt')]
#[HtmxOnly]
public function requestPrompt(HtmxRequest $htmx): HtmxResponse
{
$promptValue = $htmx->prompt ?? '(no prompt provided)';
return HtmxResponseBuilder::create($htmx->isHtmx)
->success()
->viewBlock('request_showcase.html.twig', 'promptResult', ['htmx' => $htmx, 'promptValue' => $promptValue])
->viewBlock('request_showcase.html.twig', 'requestInfoOob', ['htmx' => $htmx])
->build();
}
/**
* Demonstrates replaceUrl - changes currentUrl for subsequent requests
*/
#[Route('/url', name: 'app_request_showcase_url')]
#[HtmxOnly]
public function url(HtmxRequest $htmx): HtmxResponse
{
$newUrl = '/request-showcase?demo=url&t=' . time();
return HtmxResponseBuilder::create($htmx->isHtmx)
->success()
->viewBlock('request_showcase.html.twig', 'urlResult', ['htmx' => $htmx, 'newUrl' => $newUrl])
->viewBlock('request_showcase.html.twig', 'requestInfoOob', ['htmx' => $htmx])
->replaceUrl($newUrl)
->build();
}
/**
* Demonstrates pushUrl for history restoration test
*/
#[Route('/history', name: 'app_request_showcase_history')]
#[HtmxOnly]
public function history(HtmxRequest $htmx): HtmxResponse
{
$newUrl = '/request-showcase?history=' . time();
return HtmxResponseBuilder::create($htmx->isHtmx)
->success()
->viewBlock('request_showcase.html.twig', 'historyResult', ['htmx' => $htmx, 'newUrl' => $newUrl])
->viewBlock('request_showcase.html.twig', 'requestInfoOob', ['htmx' => $htmx])
->pushUrl($newUrl)
->build();
}
/**
* Demonstrates boosted request
*/
#[Route('/boosted', name: 'app_request_showcase_boosted', methods: ['GET', 'POST'])]
public function boosted(HtmxRequest $htmx): HtmxResponse|RedirectResponse
{
if (!$htmx->isHtmx) {
return $this->redirectToRoute('app_request_showcase');
}
return HtmxResponseBuilder::create($htmx->isHtmx)
->success()
->viewBlock('request_showcase.html.twig', 'boostedResult', ['htmx' => $htmx])
->viewBlock('request_showcase.html.twig', 'requestInfoOob', ['htmx' => $htmx])
->build();
}
}
{% block requestInfo %}
<figure>
<table>
<thead>
<tr>
<th>Property</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td class="prop-name">isHtmx</td>
<td><span class="prop-badge {{ htmx.isHtmx ? 'prop-true' : 'prop-false' }}">{{ htmx.isHtmx ? 'true' : 'false' }}</span></td>
</tr>
<tr>
<td class="prop-name">isBoosted</td>
<td><span class="prop-badge {{ htmx.isBoosted ? 'prop-true' : 'prop-false' }}">{{ htmx.isBoosted ? 'true' : 'false' }}</span></td>
</tr>
<tr>
<td class="prop-name">currentUrl</td>
<td style="font-family: monospace; font-size: 0.75rem; word-break: break-all;">{{ htmx.currentUrl ?? '(null)' }}</td>
</tr>
<tr>
<td class="prop-name">target</td>
<td style="font-family: monospace; font-size: 0.75rem;">{{ htmx.target ?? '(null)' }}</td>
</tr>
<tr>
<td class="prop-name">trigger</td>
<td style="font-family: monospace; font-size: 0.75rem;">{{ htmx.trigger ?? '(null)' }}</td>
</tr>
<tr>
<td class="prop-name">triggerName</td>
<td style="font-family: monospace; font-size: 0.75rem;">{{ htmx.triggerName ?? '(null)' }}</td>
</tr>
<tr>
<td class="prop-name">prompt</td>
<td style="font-family: monospace; font-size: 0.75rem;">{{ htmx.prompt ?? '(null)' }}</td>
</tr>
<tr>
<td class="prop-name">isForHistoryRestoration</td>
<td><span class="prop-badge {{ htmx.isForHistoryRestoration ? 'prop-true' : 'prop-false' }}">{{ htmx.isForHistoryRestoration ? 'true' : 'false' }}</span></td>
</tr>
</tbody>
</table>
</figure>
{% endblock %}
{% block requestInfoOob %}
<div id="request-info" hx-swap-oob="true">
{{ block('requestInfo') }}
</div>
{% endblock %}
{% block actionResult %}
<div style="color: var(--pico-ins-color);">
<strong>Request received!</strong>
trigger={{ htmx.trigger ?? 'null' }}, triggerName={{ htmx.triggerName ?? 'null' }}, target={{ htmx.target ?? 'null' }}
</div>
{% endblock %}
{% block promptResult %}
<div style="color: var(--pico-ins-color);">
<strong>Prompt value:</strong> {{ promptValue }}
</div>
{% endblock %}
{% block urlResult %}
<div style="color: var(--pico-del-color);">
<strong>URL replaced:</strong> {{ newUrl }}<br>
<small>Click other buttons - currentUrl will show this new URL</small>
</div>
{% endblock %}
{% block historyResult %}
<div style="color: var(--pico-primary);">
<strong>URL pushed:</strong> {{ newUrl }}<br>
<small>Now click browser back to see isForHistoryRestoration = true</small>
</div>
{% endblock %}
{% block boostedResult %}
<div id="boosted-response" style="color: var(--pico-ins-color);">
<strong>Boosted request received!</strong> isBoosted = {{ htmx.isBoosted ? 'true' : 'false' }}
</div>
{% endblock %}