This site is built with Viewi itself. It is experimental and still in development. If you see any bugs please do not hesitate and open an issue or DM me on Twitter.

Viewi

Build full-stack and completely reactive user interfaces with PHP

Get Started

Why Viewi ?

Typical JavaScript frameworks require you to write a lot of extra code. From learning frameworks like Vue js, to the fact that your SEO will suffer, it's just more trouble than it's worth.

When developing a web application, you either use a javascript framework or go with a backend template engine and lose reactivity. To get both at the same time requires some complex manipulations, and it's not always efficient.

Viewi allows you to create reactive web applications using your favorite PHP. It converts your code into native javascript code to run it in the browser. This way, you get a perfectly rendered HTML page on the first load, and at the same time, your page will remain reactive without requesting each next page on link clicks, etc.

It's a new approach to writing web applications that target both sides: server and browser. Do not sacrifice one for another.

How Viewi Works

Viewi takes your components with templates and converts them into special HTML tokens and JavaScript code. This way you don't need to duplicate your logic twice. And it keeps to be SEO friendly and fully dynamic out of the box.

Viewi is not bound to specific framework and has its own template engine which is so simple to use. It also has built in Router and renders new pages without interaction with the server.

<?php

namespace Application\Components\Views\Demo\TodoApp;

use Viewi\BaseComponent;
use Viewi\DOM\Events\DOMEvent;

class TodoApp extends BaseComponent
{
    public string $text = '';
    public array $items = [];

    public function handleSubmit(DOMEvent $event)
    {
        $event->preventDefault();
        if (strlen($this->text) == 0) {
            return;
        }
        $this->items[] = $this->text;
        $this->text = '';
    }
}
var TodoApp = function () {
    var $this = this;
    this.text = '';
    this.items = [];
    
    this.handleSubmit = function (event) {
        event.preventDefault();
        if(strlen($this.text) == 0) {
             return ;
        }
        $this.items.push($this.text);
        notify($this.items, 'push');
        $this.text = '';
    };
};

Html template

<div>
    <h2>Todo</h2>
    <form (submit)="handleSubmit($event)">
        <label for="new-todo">What needs to be done?</label>
        <input id="new-todo" type="text" model="$text"
         autocomplete="off">
        <button>
            Add #{count($items) + 1}
        </button>
    </form>
    <TodoList items="$items" />
</div>

See Viewi in Action

A Simple Component

Viewi component takes all the input data and assigns them to public properties. To render property in the HTML just use it as a PHP variable.

HelloMessage.php
<?php

namespace Application\Components\Views\Demo\SimpleComponent;

use Viewi\BaseComponent;

class HelloMessage extends BaseComponent
{
    public string $name;
}
HelloMessage.html
<div>Hello $name</div>
Usage
<HelloMessage name="James" />
Result
Hello James

A Stateful Component

Viewi component can maintain its state and update the view once it is changed.

Timer.php
<?php

namespace Application\Components\Views\Demo\TimerComponent;

use Viewi\BaseComponent;
use Viewi\Components\Services\ClientTimer;

class Timer extends BaseComponent
{
    public ClientTimer $timer;
    public int $seconds = 0;
    public int $timerId = 0;

    public function __init(ClientTimer $timer)
    {
        $this->timer = $timer;
        $this->timerId = $timer->setInterval(fn () => $this->tick(), 1000);
    }

    public function __destroy()
    {
        $this->timer->clearInterval($this->timerId);
    }

    public function tick()
    {
        $this->seconds++;
    }
}
Timer.html
<div>
    Seconds: $seconds
</div>
Usage
<Timer />
Result
Seconds: 0

A Reactive Component

Components in Viewi react to events caused by browser or user interactions. Any PHP expression can be easily assigned as an event handler. All the changes will update HTML accordingly.

Counter.php
<?php

namespace Application\Components\Views\Demo\ReactiveComponent;

use Viewi\BaseComponent;

class Counter extends BaseComponent
{
    public int $count = 0;

    public function increment()
    {
        $this->count++;
    }
}
Counter.html
<button (click)="increment()">Clicked $count times.</button>
Usage
<Counter />
Result

An Application

By combining different components together we can create a simple Todo application. Each component will have its own logic without affecting the parent application. Data can be passed to the child component through properties. Event handlers track all the changes that user has made.

TodoApp.php
<?php

namespace Application\Components\Views\Demo\TodoApp;

use Viewi\BaseComponent;
use Viewi\DOM\Events\DOMEvent;

class TodoApp extends BaseComponent
{
    public string $text = '';
    public array $items = [];

    public function handleSubmit(DOMEvent $event)
    {
        $event->preventDefault();
        if (strlen($this->text) == 0) {
            return;
        }
        $this->items[] = $this->text;
        $this->text = '';
    }
}
TodoApp.html
<div>
    <h2>Todo</h2>
    <form (submit)="handleSubmit($event)">
        <label for="new-todo">What needs to be done?</label>
        <input id="new-todo" type="text" model="$text" autocomplete="off">
        <button>
            Add #{count($items) + 1}
        </button>
    </form>
    <TodoList items="$items" />
</div>
TodoList.php
<?php

namespace Application\Components\Views\Demo\TodoApp;

use Viewi\BaseComponent;

class TodoList extends BaseComponent
{
    public array $items;
}
TodoList.html
<ul>
    <li foreach="$items as $item">$item</li>
</ul>
Usage
<TodoApp />
Result

Todo

A Component Using External Javascript

Viewi allows you to use custom JavaScript which makes it possible to integrate with external libraries and frameworks. For example, using marked library we convert the text into markdown HTML in real time.

Include marked.min.js
...
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
...
MarkdownJs.php
<?php

namespace Application\Components\Views\Demo\ExternalIntegration;

use Viewi\BaseComponent;

class MarkdownJs extends BaseComponent
{
    public string $markText = '# Marked in browser\n\nRendered by **marked**.';

    public function getMarkedHtml($text){
        // to insert custom javascript
        // just use heredoc syntax with `javascript` keyword
        <<<javascript
        return marked.parse(text);
        javascript;
    }
}
MarkdownJs.html
<h3>Input</h3>
<label for="markdown-text">
    Enter some markdown
</label>
<textarea id="markdown-text" model="$markText"></textarea>
<h3>Output</h3>
<div>{{getMarkedHtml($markText)}}</div>
Usage
<MarkdownJs />
Result

Input

Output

Excited? Then let's Get Started