Components and inherited scope
A PHPue component is any
.pvuefile pulled in with@require Name 'path.pvue';and placed with<Name/>. At compile time its PHP, markup and cscript are inlined where the tag is, so it runs in the parent's scope and sees every variable the parent has, exactly like a PHPinclude. That is why PHPue has no props, slots or emits: set a variable before the tag, and the component reads it.
Declare and place
<!-- views/products.pvue -->
<script>
@require ProductCard 'components/cards/ProductCard.pvue';
@require EmptyState 'components/EmptyState.pvue';
$products = [
['name' => 'Tea', 'price' => 3.5],
['name' => 'Coffee', 'price' => 4.0],
];
</script>
<template>
<section class="grid">
<ProductCard p-for="$product in $products" />
</section>
<EmptyState p-if="count($products) === 0"/>
</template><!-- components/cards/ProductCard.pvue -->
<script>
// $product comes from the parent's p-for. Default it so the card also works on its own.
$product = $product ?? ['name' => 'Unknown', 'price' => 0];
</script>
<template>
<article class="card">
<h3>{{ $product['name'] }}</h3>
<p>£{{ number_format($product['price'], 2) }}</p>
</article>
</template>@requiretakes single or double quotes; the path is relative to the web root (www/).- Tags:
<Name/>,<Name />or<Name></Name>. No other attributes and no children. The only exceptions arep-ifandp-foron the tag (0.0.3). - Components can
@requireother components. Paths are always from the web root. - A missing file compiles to a comment and renders nothing. Using
<Name/>without@requirerenders nothing useful either.
Giving a component data (instead of props)
Set variables right before the tag, in raw PHP:
<?php
$panelTitle = 'Traffic';
$panelRows = $trafficRows;
?>
<StatsPanel/>
<?php
$panelTitle = 'Revenue';
$panelRows = $revenueRows;
?>
<StatsPanel/>// components/StatsPanel.pvue <script>: normalise what you inherit
$panelTitle = $panelTitle ?? '';
$panelRows = (isset($panelRows) && is_array($panelRows)) ? $panelRows : [];- Use specific names (
$panelTitle, not$title): a component's writes go straight into the parent scope and stay visible after it. - Inside a loop no setup is needed: the component reads the loop variable.
- Do not invent a props mechanism (attributes, a
$propsarray,extract()). It does not exist and is not needed.
Rules for component code
- No named functions or classes in a component's
<script>. The code runs once per placement, so a second use (or ap-for) fails withCannot redeclare. Put helpers inbackend/or use closures ($fmt = fn($n) => number_format($n, 2);). <header>is ignored in components. Put CSS in a<style>inside the component's template, or in the App/view header.<cscript>repeats every time the component renders. Top-levellet/constthen throwsIdentifier has already been declared. Usefunctiondeclarations,var, or wrap the code in an IIFE with a guard:
(function () {
if (window.__cartWidgetReady) return;
window.__cartWidgetReady = true;
document.addEventListener('click', function (e) { /* ... */ });
})();@AJAXin components works in 0.0.3 (handlers are global and built into.dist/ajax/ajax-component-<Name>.php). Keeping handlers in the view that hosts the component is still the clearest layout.- Avoid these variable names, which the compiled preamble resets:
$routing,$current_route,$b404,$input,$action.
Components in App.pvue vs views
Components placed in App.pvue (navbar, footer, cookie banner) run in the App scope; components placed in a view run in that view's scope. App variables are not visible in views and vice versa. Share data through a backend/ singleton, $_SESSION or define() (backend and scope).
Rendering one component on its own (partials)
Components only exist inlined at compile time. To return one component's HTML from an @AJAX handler (for HTMX or fetch + innerHTML), compile it with convert_pvue_file() in a backend helper and pass the variables it would normally inherit. See the PHPueFragment helper in cscript, AlpineJS, x-ssr and HTMX.