Getting started
Official guide to install SERIXA, render your first page, and use widgets, themes, and layouts.
Requirements: PHP 8.2+ · Composer
Related: Getting started · First blog · CLI · Troubleshooting
From a clone of this monorepo:
cd packages/cli
composer install
php bin/serixa new my-app
cd my-app
composer install
php -S localhost:8000 -t publicOr, once serixa/cli is available as a dependency:
composer require --dev serixa/cli
vendor/bin/serixa new my-appGenerate apps outside the framework tree (sibling folders). Path repositories that nest inside framework/ can recurse — see Path-repository recursion.
composer require serixa/frameworkOptional scaffolding tooling:
composer require --dev serixa/cliUntil you publish to Packagist, point Composer at the local framework/ folder:
{
"repositories": [
{
"type": "path",
"url": "../path/to/Serixa/framework",
"options": { "symlink": true }
}
],
"require": {
"php": "^8.2",
"serixa/framework": "*@dev"
}
}Then:
composer update serixa/frameworkConfirm the junction targets framework/, not the monorepo root. A wrong path surfaces as Class "Serixa\Component\…" not found.
php -r "require 'vendor/autoload.php'; echo class_exists(Serixa\\Rendering\\Renderer::class) ? 'OK' : 'FAIL';"Create public/index.php (or edit the file generated by serixa new):
<?php
declare(strict_types=1);
require dirname(__DIR__) . '/vendor/autoload.php';
use Serixa\Component\Button;
use Serixa\Assets\StyleSheet;
use Serixa\Component\Text;
use Serixa\Document\Document;
use Serixa\Rendering\Renderer;
use Serixa\Runtime\ClientRuntime;
$document = Document::make()
->title('Hello SERIXA')
->description('My first page')
->body(
Text::make('Hello, SERIXA')->as('h1')->size('xl')->weight('bold'),
Text::make('Write PHP. Get semantic HTML.')->tone('muted'),
Button::make('Continue')->primary(),
);
StyleSheet::register($document->assets());
ClientRuntime::register($document->assets());
header('Content-Type: text/html; charset=utf-8');
echo (new Renderer())->render($document);Serve from public/ (run serixa build or composer build first so /assets/serixa.css exists):
vendor/bin/serixa build
php -S localhost:8000 -t publicOpen http://localhost:8000. The document root must be public/ so assets resolve correctly.
SERIXA is server-side only. Renderer turns a widget tree or a full Document into an HTML string.
use Serixa\Component\Button;
use Serixa\Rendering\Renderer;
$html = (new Renderer())->render(
Button::make('Save')->primary(),
);Use fragments inside controllers, existing templates, or email bodies.
Document)use Serixa\Document\Document;
use Serixa\Rendering\Renderer;
$document = Document::make()
->title('Dashboard')
->description('Admin')
->language('en')
->body(/* widgets */);
echo (new Renderer())->render($document);Document owns <title>, meta tags, favicon, Open Graph fields, and registered CSS/JS via $document->assets().
If you use Modal, Dropdown, Tabs, Toast, and similar, register the client runtime:
use Serixa\Runtime\ClientRuntime;
ClientRuntime::register($document->assets(), '/assets/runtime/serixa.js');serixa new copies serixa.js into public/assets/runtime/. Serving from anywhere except public/ causes a 404 — see Runtime script 404.
Widgets are PHP objects under Serixa\Component\. Build them with ::make() and fluent modifiers:
use Serixa\Component\Button;
use Serixa\Component\Card;
use Serixa\Component\Column;
use Serixa\Component\Text;
Column::make([
Text::make('Welcome')->as('h1')->size('xl')->weight('bold'),
Card::make()
->title('Getting started')
->child(Text::make('Compose widgets. SERIXA renders HTML.')->tone('muted')),
Button::make('Save')->primary()->large(),
]);| Area | Examples | Docs |
|---|---|---|
| Layout | Row, Column, Center, Padding, Scaffold | layout-engine.md |
| Content | Text, Button, Card, Link, Container | ui-foundation.md |
| Forms | Form, Input, Label, Checkbox | ui-foundation.md |
| Data | DataTable, Pagination, Badge, StatCard | data-display.md |
| Interactive | Modal, Tabs, Dropdown, Toast | interactive-components.md |
Widgets do not hardcode CSS classes. Variants like ->primary() and ->large() become theme state; the theme maps that state to Tailwind utilities.
DefaultTheme maps (component, state) → Tailwind class lists. Widgets resolve styles through ThemeManager during render.
use Serixa\Theme\DefaultTheme;
use Serixa\Theme\ThemeManager;
// Default — applied automatically
ThemeManager::current(); // DefaultTheme
// Swap for the whole request
ThemeManager::set(new DefaultTheme());Inspect a mapping:
$classes = ThemeManager::current()->classes('button', [
'variant' => 'primary',
'size' => 'lg',
]);serixa make:theme DarkSerixa\Contracts\ThemeInterface (or extend DefaultTheme and override maps)ThemeManager::set(new App\Themes\DarkTheme()) before renderUnknown component keys or invalid variants throw InvalidArgumentException — intentional fail-fast. See Unknown theme state.
Theme classes still need CSS in the browser: run serixa build to emit /assets/serixa.css offline (assets.md).
use Serixa\Component\Center;
use Serixa\Component\Column;
use Serixa\Component\SizedBox;
use Serixa\Component\Text;
Center::make(
Column::make([
Text::make('Welcome')->as('h1'),
SizedBox::height(4),
Text::make('Centered column layout.')->tone('muted'),
])->gap(2)->alignCenter(),
);Scaffold)use Serixa\Component\AppBar;
use Serixa\Component\Footer;
use Serixa\Component\Link;
use Serixa\Component\MainContent;
use Serixa\Component\Scaffold;
use Serixa\Component\Sidebar;
use Serixa\Component\Text;
Scaffold::make()
->appBar(AppBar::make()->title('Dashboard'))
->sidebar(
Sidebar::make(
Link::make('Home')->href('/')->active(),
Link::make('Settings')->href('/?page=settings'),
),
)
->body(
MainContent::make(
Text::make('Page content')->as('h1')->size('xl')->weight('bold'),
),
)
->footer(Footer::make('© 2026'));Scaffold emits landmarks (<header>, <aside>, <main>, <footer>). Prefer wrapping page bodies in a shared layout class (as serixa new does with layouts/AppLayout.php) so chrome stays consistent across routes.
SERIXA does not include a router — wire pages yourself (typically match ($_GET['page']) in public/index.php). See first-blog.md.
Minimal end-to-end app: install, one page, layout, offline CSS build, render.
composer.json
{
"name": "app/hello-serixa",
"require": {
"php": "^8.2",
"serixa/framework": "^1.0"
},
"autoload": {
"psr-4": {
"App\\": "src/"
}
}
}Use a path repository while developing against this monorepo.
public/index.php
<?php
declare(strict_types=1);
require dirname(__DIR__) . '/vendor/autoload.php';
use Serixa\Component\AppBar;
use Serixa\Component\Button;
use Serixa\Component\Card;
use Serixa\Component\Column;
use Serixa\Component\Footer;
use Serixa\Component\MainContent;
use Serixa\Component\Scaffold;
use Serixa\Component\Text;
use Serixa\Document\Document;
use Serixa\Rendering\Renderer;
$shell = Scaffold::make()
->appBar(AppBar::make()->title('Hello'))
->body(
MainContent::make(
Column::make([
Text::make('Installed successfully')->as('h1')->size('xl')->weight('bold'),
Card::make()
->title('Next steps')
->child(Text::make('Add pages, a layout class, and your own routing.')->tone('muted'))
->child(Button::make('Continue')->primary()),
])->gap(4),
),
)
->footer(Footer::make('SERIXA'));
$document = Document::make()
->title('Hello · SERIXA')
->body($shell);
StyleSheet::register($document->assets());
ClientRuntime::register($document->assets());
header('Content-Type: text/html; charset=utf-8');
echo (new Renderer())->render($document);composer install
vendor/bin/serixa build
php -S localhost:8000 -t publicpublic/ — keep vendor/, pages, and config outside the document root.Renderer produce markup.Scaffold / AppLayout for chrome; page classes return Document.->primary(), ->tone('muted')).serixa build so /assets/serixa.css exists; see styling.md and deployment.md.ClientRuntime only when needed — static pages need zero JavaScript.framework/ when using path repositories.Serixa\Component\*, Document, Renderer, ThemeManager. Avoid @internal types.composer qa (PHPUnit + PHPStan + PHP-CS-Fixer) before releasing.| Symptom | Likely cause | Fix |
|---|---|---|
Class "Serixa\…" not found | Autoload / wrong path junction | composer dump-autoload; ensure path repo URL is …/framework |
| Interactive widgets do nothing | Missing or 404 serixa.js | Serve -t public; check runtime script 404 |
| No styles / unstyled page | Missing compiled CSS | Run serixa build; register StyleSheet::register() → /assets/serixa.css |
Unknown theme component | Typo or unsupported variant | Use allowed variants/sizes; see unknown theme state |
composer install hangs in monorepo | Nested path recursion | Generate apps as siblings; path-repository recursion |
| Package not found on Packagist | Local-only / unpublished | Use a path repository against this clone |
PE onClick attributes ignored | Attributes only — no handler | Register Serixa.on('name', …) after the runtime loads |
Full reference: troubleshooting.md · faq.md
| Goal | Doc |
|---|---|
| Mental model & scope | getting-started.md |
| 30-minute blog | first-blog.md |
| Admin shell | first-dashboard.md |
| Deploy | deployment.md |
| CLI commands | cli.md |