Tempest View is an extension on the most popular templating engine of all time: HTML. Template inheritance and inclusion are modelled via HTML elements; data binding and control structures are handled with attributes.
<x-base title="Home"> <x-post :foreach="$this->posts as $post"> {!! $post->title !!} <span :if="$this->showDate($post)"> {{ $post->date }} </span> <span :else> - </span> </x-post> <div :forelse> <p>It's quite empty here…</p> </div> <x-footer /> </x-base>
Tempest's discovery is built-in — that's a given. Just create a
.view.php
file, and Tempest takes care of the rest. No config needed, your components are available everywhere.
<!-- x-base.view.php --> <x-component name="x-base"> <html lang="en"> <head> <title :if="$title ?? null">{{ $title }} | Tempest</title> <title :else>Tempest</title> <x-slot name="styles" /> </head> <body class="relative font-sans antialiased"> <x-slot/> <x-slot name="scripts" /> </body> </html> </x-component>
Just in case you need that little extra, PHP is there to help you out.
<?php /** @var \App\Front\BlogPostView $this */ use App\Front\Meta\MetaImageController; use function Tempest\uri; $title = 'Click me!'; ?> <a :href="uri(MetaImageController::class, $this->type)"> {{ $title }} </a>
We need help!
We would like to have proper IDE support for Tempest View before tagging version 1.0. If you're familiar with implementing LSPs or building IntelliJ plugins, feel free to contact us via Discord or email to discuss the details.
When anonymous components aren't enough, you can fall back to full-blown PHP implementations. Coming in the future: these components will be the entry point for reactive components similar to Livewire or HTMX.
final readonly class Input implements ViewComponent { public function __construct( private Session $session, ) { } public static function getName(): string { return 'x-input'; } public function compile(ViewComponentElement $element): string { $name = $element->getAttribute('name'); $label = $element->getAttribute('label'); $type = $element->getAttribute('type'); $default = $element->getAttribute('default'); $errorHtml = ''; if ($errors = ($this->session->get(Session::VALIDATION_ERRORS)[$name] ?? null)) { $errorHtml = '<div>' . implode('', array_map( fn (Rule $failingRule) => "<div>{$failingRule->message()}</div>", $errors, )) . '</div>'; } $original = $this->session->get(Session::ORIGINAL_VALUES)[$name] ?? $default; return <<<HTML <div> <label for="{$name}">{$label}</label> <input type="{$type}" name="{$name}" id="{$name}" value="{$original}" /> {$errorHtml} </div> HTML; } // … }