Get started

Build your first Thorm page and learn the basic workflow: structure, state, and rendering mode.

Prerequisites

  • PHP: >= 8.1
  • A local web server (built-in PHP server is fine)
  • Composer (optional)

Installation

If you have composer installed:

Console
composer create-project bitforge/thorm-app hello-thorm
cd hello-thorm

For this tutorial we are going to assume the following folder structure:

Console
/hello-thorm
/.git
/public
    .delete-me
/src
    /assets
        .delete-me
composer.json
README.md

Let's download Thorm framework. You should check for safety https://packagist.org/packages/bitforge/thorm to be sure you are on the latest version, until we reach a stable release.

Console
composer require bitforge/thorm:^0.1@alpha

At this point, it might be a good idea to register the namespaces.

Console
composer dump-autoload
Step 1

Create a tiny Thorm page

Start with a minimal page that renders a heading and a button. Save the file in the root of your project, e.g. /hello-thorm

PHP


// src/home.php
<?php
declare(strict_types=1);

require 
__DIR__ '/../vendor/autoload.php';

use function 
Thorm\{h1p};

function 
green($s){ return "\033[32m{$s}\033[0m"; }
function 
red($s){ return "\033[31m{$s}\033[0m"; }

return 
fragment([
  
h1([], [ text('Hello Thorm') ]),
  
p([], [ text('Modern UI, backend workflow.')]),
]);
Step 2

Add state + an interaction

Use an atom for state and update it through an event/action. This is the core β€œreactive UI without custom JavaScript” loop.

PHP


// src/home.php
<?php

...

$count state(0);                                     // new

$app fragment([
  
h1([], [ text('Hello Thorm') ]),
  
p([], [text('Modern UI, backend workflow.')]),
  
p([], [text(concat('Count: 'read($count)))]),     // new
  
button([                                            // new
    
on('click'inc($count1)),                      // new
  
], [text('Increment')]),                            // new
]);
Tip: keep the first interaction tiny. Once the mental model clicks, everything else is composition.
Step 3

Render the html + IR files

Thorm needs to generate the html and intermediate representation. Will at this by adding code to write two files in the root of our project: index.html and 7dd1e06894b2ffa9ab8bd38bda16e619.ir.json where is an unique ID assigned at render time.

PHP


// src/home.php
<?php
...
use 
Thorm\Renderer;                                                                 // new

$app fragment([
  ...
]);

$renderer = new Render();                                                          // new
$res $renderer->render($app);                                                  // new

$build BuildExample::build([                                                     // new
    
'name'          => strtolower(pathinfo(__FILE__PATHINFO_FILENAME)),           // new
    
'path'          => __DIR__.'/../public/',                                       // new
    
'renderer'      => $res,                                                       // new
    
'template'      => __DIR__.'/../vendor/bitforge/thorm/assets/index.tpl.html',   // new
    
'opts'          => [                                                            // new
        
'title'         => 'Hello Thorm',                                           // new
        
'containerId'   => 'app',                                                   // new
    
],                                                                              // new
]);                                                                                 // new

if($build !== false ) {                                                            // new
    
echo green("File wrote to disk.
"
);                                          // new
} else {                                                                            // new
    
echo red("Could not write files to disk.
"
);                                 // new
}                                                                                   // new
Step 4

Final code

Here is the final code. This was intentionally kept simple, without the help of any PHP framework, to make it easier to understand the code.

PHP


// src/home.php
<?php
declare(strict_types=1);

require 
__DIR__ '/../vendor/autoload.php';

use function 
Thorm\{buttontextconcatfragmenth1onreadstateincp};
use 
Thorm\Render;
use 
Thorm\BuildExample;

function 
green($s){ return "\033[32m{$s}\033[0m"; }
function 
red($s){ return "\033[31m{$s}\033[0m"; }

$count state(0);

$app fragment([
  
h1([], [ text('Hello Thorm') ]),
  
p([], [text('Modern UI, backend workflow.')]),
  
p([], [text(concat('Count: 'read($count)))]),
  
button([
    
on('click'inc($count1)),
  ], [
text('Increment')]), 
]);

$renderer = new Render();
$res $renderer->render($app);

$build BuildExample::build([
    
'name'          => strtolower(pathinfo(__FILE__PATHINFO_FILENAME)),
    
'path'          => __DIR__.'/../public/',
    
'renderer'      => $res,
    
'template'      => __DIR__.'/../vendor/bitforge/thorm/assets/index.tpl.html',
    
'opts'          => [
        
'title'         => 'Hello Thorm',
        
'containerId'   => 'app',
    ],
]);

if(
$build !== false ) { 
    echo 
green("File wrote to disk.\n"); 
} else { 
    echo 
red("Could not write files to disk.\n");
}
echo 
"\n";

Run the build command which is a simple cli call for the file.

Console

php HomePage.php

This is going to create/overwrite index.html and <...>.ir.json files in public/home

You can run the built-in php webserver or deploy the /hello-thorm under a real webserver like Apache/nginx. If you prefer localhost dev, here is the command:

Console

php -S localhost:8000
Status: Developer Preview
Things may change, things might break. If something feels awkward, it's probably a design edge we're still smoothing out.