Template

The template engine allows to write typesafe, fast and secure HTML templates. It is based on JSX and is ready to use as soon as you use the .tsx file extension and adjust the tsconfig.json accordingly.

The important thing is: it is not compatible with React. As soon as React is to be used, @deepkit/template is incompatible. Deepkit's template engine is only intended for SSR (server-side rendering).

Installation

In your tsconfig you have to adjust following settings: jsx and jsxImportSource

{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "target": "ES2020",
    "moduleResolution": "node",

    "jsx": "react-jsx",
    "jsxImportSource": "@deepkit/template"
  }
}

Now you can use JSX directly in your controller.

import { App } from '@deepkit/app';
import { FrameworkModule } from '@deepkit/framework';
import { http } from '@deepkit/http';

@http.controller('my-base-url/')
class MyPage {
    @http.GET('hello-world')
    helloWorld() {
        return <div style="color: red">Hello World</div>;
    }
}

new App({
    controllers: [MyPage],
    imports: [
        new FrameworkModule({
            debug: true,
        })
    ]
}).run();

When you return such a JSX in your route method, the HTTP content type is automatically set to texthtml; charset=utf-8.

Components

You can structure your templates the way you are used to in React. Either modularize your layout into multiple function or class components.

Function Components

The easiest way is to use a function that returns JSX.

async function Website(props: {title: string, children?: any}) {
    return <html>
        <head>
            <title>{props.title}</title>
        </head>
        <body>
            {props.children}
        </body>
    </html>;
}

class MyPage {
    @http.GET('hello-world')
    helloWorld() {
        return <Website title="Hello world">
            <h1>Great page</h1>
        </Website>;
    }
}
$ curl http://localhost:8080/hello-world
<html><head><title>Hello world</title></head><body><h1>Great page</h1></body></html>

Function components can be asynchronous (unlike in React). This is an important difference from other template engines you may be familiar with, like React.

All functions have access to the dependency injection container and can reference any dependencies starting with the third parameter.

class Database {
    users: any[] = [{ username: 'Peter' }];
}

function UserList(props: {}, database: Database) {
    return <div>{database.users.length}</div>;
}

class MyPage {
    @http.GET('list')
    list() {
        return <UserList/>
    }
}

new App({
    controllers: [MyPage],
    providers: [Database],
    imports: [new FrameworkModule()]
}).run();

Class Components

An alternative way to write a component is a class component. They are handled and instantiated in the Dependency Injection container and thus have access to all services registered in the container. This makes it possible to directly access a data source such as a database in your components, for example.

class UserList {
    constructor(
        protected props: {},
        protected children: any,
        protected database: SQLiteDatabase) {
    }

    async render() {
        const users = await this.database.query(User).find();

        return <div class="users">
            {users.map((user) => <UserDetail user={user}/>)}
        </div>;
    }
}

class MyPage {
    @http.GET('')
    listUsers() {
        return <UserList/>;
    }
}

For class components the first constructor arguments are reserved. props can be defined arbitrarily, children is always "any", and then optional dependencies follow, which you can choose arbitrarily. Since class components are instantiated in the Dependency Injection container, you have access to all your services.

Dynamic HTML

The template engine has automatically cleaned up all the variables used, so you can safely use user input directly in the template. To render dynamic HTML, you can use the html function.

import { html } from '@deepkit/template';
function helloWorld() {
    const yes = "<b>yes!</b>";
    return <div style="color: red">Hello World. {html(yes)}</div>;
}

Optimization

The template engine tries to optimize the generated JSX code so that it is much easier for NodeJSV8 to generate the HTML string. For this to work correctly, you should move all your components from the main app.tsx file to separate files. A structure might look like this:

.
├── app.ts
└── views
    ├── user-detail.tsx
    ├── user-list.tsx
    └── website.tsx