Click here to Skip to main content
15,436,765 members
Articles / Web Development / HTML
Posted 8 Jan 2020



Bobril - VII - Components and TSX

Rate me:
Please Sign up or sign in to vote.
5.00/5 (2 votes)
8 Jan 2020CPOL3 min read
This article will guide you in creating of bobril application using TSX components.


In the sixth article, we have created a simple TODO application with bobx and composed by general components created by b.createComponent. In the last year, bobril has evolved in the react-like way and brought full support of TSX, stateful class components and stateless functional components.

TSX (type safe JSX) is a template language extending TypeScript. It is used to define UI and logic in one place. It produces Bobril elements.

This article will introduce how to create a TODO app with components of both types and using TSX.

Prepare Project

Create project in a similar way as in the previous examples:

npm i bobril-build -g
npm init
npm i bobril bobx --save

Add index.tsx and run:


Functional Components

The simplest component is the functional one. It does not keep any state and just reflects the input data. It is defined by a function with one argument for data and returning the b.IBobrilNode. To follow the rules for TSX components, the name of function has to start with a capitalized letter. Data are defined the same way as usual.

Create a components/listItem.tsx and write the following code:

import * as b from "bobril";

export interface IItem {
  id: number;
  text: string;
  done: boolean;

export interface IItemData extends IItem {
  index: number;
  onItemChecked(index: number, value: boolean): void;

export function ListItem(data: IItemData): b.IBobrilNode {
  return (
    <li key={} style={data.done && strikeOut}>
        onChange={value => data.onItemChecked(data.index, value)}

const strikeOut = b.styleDef({ textDecoration: "line-through" });

You can see that TSX definition of component is pretty easy. You can use native elements and fill its attributes with data as an {expression}.

In this component, you can also see the definition of bobril key and styling.

Such component can be used as a TSX element. Put the following code to components/list.tsx:

import * as b from "bobril";
import { ListItem, IItem } from "./listItem";

export interface IListData {
  items: IItem[];
  onItemChecked(index: number, value: boolean): void;

export function List(data: IListData): b.IBobrilNode {
  return (
    <ul style={noBullets}>
      {, index) => (
        <ListItem {...item} index={index} onItemChecked={data.onItemChecked} />

const noBullets = b.styleDef({ listStyleType: "none" });

This is a great example of how you can generate a content with inline TypeScript function map. This function maps input data into a list of TSX elements.

The next pattern you can see is usage of spread operator {...item} to provide data as attributes to child node.

Class Components

The second component type is stateful class component. Because it is a class, it allows you to keep inner state.

Create a component for form in components/form.tsx:

import * as b from "bobril";
import { observable } from "bobx";

export interface IFormData {
  onSubmit(value: string): void;

export class Form extends b.Component<IFormData> {
  private _value: string = "";

  render(): b.IBobrilChildren {
    return (
          onChange={newValue => this.updateValue(newValue)}
          onKeyUp={ev => ev.which === 13 && this.submit()}
        <button onClick={() => this.submit()}>OK</button>

  private updateValue(newValue: string): void {
    this._value = newValue;

  private submit(): boolean {;
    this._value = "";
    return true;

const spaceOnRight = b.styleDef({ marginRight: 5 });

You can see it is quite similar to general component definition. It has a method render() returning the b.IBobrilChildren. The data are accessible with It must be derived from b.Component. It also keeps its inner state for this._value as observable property.


In the code above, you can see <>...</> element. It is called fragment and it is used to wrap child elements into a virtual node. It is basically used to define components returning more than one root element.


Sometimes, you need to create special components for layouts. If the data definition contains property children: b.IBobrilChildren, then it can be simply added in the form of expression as a TSX content {data.children}.

If you need to compose more difficult layouts, then you can use a Slots pattern.

Create components/layout.tsx with the following code:

import * as b from "bobril";

export interface ILayoutData {
  children: {
    header: b.IBobrilChildren;
    body: b.IBobrilChildren;
    footer: b.IBobrilChildren;

export function Layout(data: ILayoutData): b.IBobrilNode {
  return (

The ILayoutData has children with complex type instead of b.IBobrilChildren. It allows you to define content by multiple specific children properties. TypeScript will keep you in correct usage so it is type safe.

BobX Store

The next thing we need is a bobx store as we know it from previous articles. Define one in store.ts:

import { observable } from "bobx";
import { IItem } from "./components/listItem";

export class TodoStore {
  private _todos: IItem[] = [];

  get list(): IItem[] {
    return this._todos;

  add(text: string): void {
    this._todos.push({ id:, text, done: false });

  edit(index: number, value: boolean): void {
    this._todos[index].done = value;

Compose the Page

Finally, we could compose the logic and components in the whole page component To do in index.tsx:

import * as b from "bobril";
import { Layout } from "./components/layout";
import { List } from "./components/list";
import { Form } from "./components/form";
import { TodoStore } from "./store";

class To do extends b.Component {
  todos = new TodoStore();

  render(): b.IBobrilChildren {
    return (
          header: <h1>TODO</h1>,
          body: (
              onItemChecked={(index, value) => this.todos.edit(index, value)}
          footer: <Form onSubmit={text => this.todos.add(text)} />

b.init(() => <To do />);

You can see it has inner state stored in todos property and it is created from components in layout using expression with object for slots content definition.

Finally, the To do component is used in b.init function as an entry point.


  • 2020-01-20: Updated example or bobril@13.1.1 and bobx@0.27.1
  • 2020-01-08: Article created for bobril@11.2.0 and bobx@0.27.1


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Written By
Team Leader Quadient
Czech Republic Czech Republic
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

-- There are no messages in this forum --