Functional user interfaces with Moon.js
Tags: ui, tutorial, webdev
Published: 2020-07-28 18:18:24

---
title: Functional user interfaces with Moon.js
published: true
date: 2020-07-22 13:00:52 UTC
tags: ui,tutorial,webdev
canonical_url: https://blog.logrocket.com/functional-uis-moon-js/
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/uzf8w05xip3cb19ikuvz.png
---
**Written by [Nirmalya Ghosh](https://blog.logrocket.com/author/nirmalyaghosh/)**✏️

In this tutorial, we’ll introduce Moon.js by understanding what it is and why it was created. We’ll then put that knowledge to use by building a blogging application, which will use the JSONPlaceholder API.

## What is Moon.js, and what problems does it solve?

[Moon.js](https://moonjs.org/) is a tiny (2KB), functional, declarative, minimal, fast library for functional user interfaces. It is based on the idea that modern applications are complex and that they very likely require third-party libraries for any effects other than updating a view.

To address this, Moon is based on pure functions, which can take input from and return output to drivers. Drivers are responsible for views, state, HTTP requests, routing, etc.

Moon comes with a bunch of features out of the box:

1. [Time](https://moonjs.org/time): The time driver provides the current time and allows us to schedule application functions to run after specific times
2. [Storage](https://moonjs.org/storage): The storage driver provides access to `localStorage` to persist data in the browser
3. [HTTP](https://moonjs.org/HTTP): The HTTP driver allows sending and receiving HTTP requests and responses
4. [Route](https://moonjs.org/route): The route driver provides access to the pathname of the route in a browser

In this tutorial, we’ll be working with the HTTP driver for fetching API using the [JSONPlaceholder API](https://jsonplaceholder.typicode.com/).

[![LogRocket Free Trial Banner](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/f760c-1gpjapknnuyhu8esa3z0jga.png?resize=1200%2C280&ssl=1)](https://logrocket.com/signup/)

## Installing Moon.js in our application

We can [install](https://moonjs.org/installation) Moon using the [CLI](https://moonjs.org/installation):

```jsx
npm install -g moon-cli
```

The above command will install moon globally on our system. More information on installing the package globally can be found on the [npm documentation](https://docs.npmjs.com/downloading-and-installing-packages-globally).

Once the above step is complete, we can bootstrap a Moon project with the following command:

```jsx
moon create introduction-to-moon.js
```

![Bootstrapping A Moon Application](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2020/07/bootstrapping-moon-app.png?resize=730%2C741&ssl=1)<figcaption id="caption-attachment-22092">Bootstrapping a Moon application.</figcaption>

The above command will create the following directory structure:

![Directory Structure Of A Moon App](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2020/07/moon-directory-structure.png?resize=730%2C538&ssl=1)<figcaption id="caption-attachment-22094">Directory structure of a generic Moon app.</figcaption>

Now, we can run the following command to install all the required dependencies and start the application:

```jsx
cd introduction-to-moon.js && npm install && npm run dev
```

At this point, our application should be up and running on [`http://localhost:8080/`](http://localhost:8080/):

![Welcome Screen Of A Moon Application](https://i1.wp.com/blog.logrocket.com/wp-content/uploads/2020/07/moon-app-welcome-screen.png?resize=730%2C593&ssl=1)<figcaption id="caption-attachment-22096">Welcome screen of a Moon application.</figcaption>

Moon gives us two scripts by default:

1. `dev` – this command will start a webpack server with live reload
2. `build` – this command will generate a production build of our application

## Adding Tailwind CSS to our application

Next, we’ll add [Tailwind CSS](https://tailwindcss.com/) to our application to provide some default styles. We can do so by adding the following line to our `src/index.html` file:

```jsx
<link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet">
```

> **Note**: This is not the recommended method of installing Tailwind CSS. More options are provided in the Tailwind CSS [documentation](https://tailwindcss.com/docs/installation/).

## Fetching data from JSONPlaceholder

Next, we’ll be adding the feature with which we’ll be able to fetch data from an API like JSONPlaceholder and show it on the browser. This API will be paginated, so we’ll fetch only the first post initially, and then, on clicking a button, we’ll fetch the subsequent posts.

JSONPlaceholder is a very easy API to use. It’s free and can be fetched using the code below:

```jsx
fetch('https://jsonplaceholder.typicode.com/posts/1')
  .then(response => response.json())
  .then(json => console.log(json))
```

This will return a JSON response like the following:

```jsx
{
  userId: 1,
  id: 1,
  title: "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
  body: "quia et suscipit suscipit recusandae consequuntur expedita et cum reprehenderit molestiae ut ut quas totam nostrum rerum est autem sunt rem eveniet architecto"
}
```

To get started with our application, let’s first define the HTML structure of the layout. We can create a new file, `src/view/home/index.js`, with the following content:

```jsx
// src/view/home/index.js

import Moon from "moon";
import navbar from "../components/navbar";
import post from "../components/post";

const { section, div } = Moon.view.m;

export default ({ data }) => {
  return (
    <div>
      <section>
        <navbar />
      </section>
      <section>
        <post data=data />
      </section>
    </div>
  );
};
```

We’ll be creating the `navbar` and `post` components next. The `navbar` component is responsible for showing a dummy navigation bar. We can create a new file, `src/view/components/navbar/index.js`, with the following content:

```jsx
// src/view/components/navbar/index.js

import Moon from "moon";

const { div, ul, li, a } = Moon.view.m;

export default () => {
  return (
    <div className="bg-gray-100 border-b">
      <div className="max-w-2xl mx-auto p-8 flex justify-between">
        <div>
          <ul class="flex">
            <li class="mr-6 font-bold">
              <a class="text-blue-500 hover:text-blue-800" href="#">
                Home
              </a>
            </li>
          </ul>
        </div>
        <div>
          <ul class="flex">
            <li class="mr-6 font-bold">
              <a class="text-blue-500 hover:text-blue-800" href="#">
                About
              </a>
            </li>
            <li class="mr-6 font-bold">
              <a class="text-blue-500 hover:text-blue-800" href="#">
                Contact
              </a>
            </li>
          </ul>
        </div>
      </div>
    </div>
  );
};
```

Next, we’ll create our `post` component. The `post` component is responsible for showing the posts fetched from the API, along with a button to fetch the next post from the API and render it on the browser. So let’s create a new file `src/view/components/post/index.js` with the following content:

```jsx
// src/view/components/post/index.js

import Moon from "moon";
import home from "../../home";

const { div, h2, p, button } = Moon.view.m;

const post = ({ data }) => {
  return (
    <div className="bg-white">
      <div className="max-w-2xl mx-auto p-8">
        <div className="mb-8">
          <h2 className="font-bold text-xl mb-2">{data.title}</h2>
          <p className="font-light">{data.body}</p>
        </div>
      </div>
    </div>
  )
}

export default post
```

This will show the post. However, let’s add a button for fetching the subsequent posts:

```jsx
// src/view/components/post/index.js

const post = ({ data }) => {
  const handleClick = () => {
    Moon.run(() => {
      const postCount = data.postCount + 1

      return({
        http: [
          {
            method: "GET",
            url: `https://jsonplaceholder.typicode.com/posts/${postCount}`,
            headers: {
              "Content-type": "application/json"
          },
            onLoad: ({ http }) => {
              const response = {...JSON.parse(http.body), postCount}

              return {
                view: <home data=response />
              };
            },
          }
        ]
      })}
    );
  };

  return (

    ....

    <button
      class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
      @click=handleClick
    >
      Fetch post {data.postCount + 1}
    </button>

    ....

  )
}
```

If we click on the button now, the `handleClick` function will be called. The `handleClick` function will fetch the next post from the API and render it on the browser. When the API request is complete, the `home` component will be rendered on the browser with the API response.

Since, this HTTP request is specific to how Moon works, let’s learn a little bit more about it. According to the Moon [documentation](https://moonjs.org/HTTP), the HTTP driver accepts a list of HTTP requests as output. Each request can have a `method`, `url`, `headers`, `body`, `responseType`, `onLoad`, and `onError` property. Only the `url` property is required.

```jsx
Moon.use({
    http: Moon.http.driver
});

Moon.run(() => ({
  http: [{
      method: "GET",  // GET, POST, PUT, DELETE, etc.
      url: "https://jsonplaceholder.typicode.com/posts/1",
      headers: {
        "Content-Type": "application/json" // Any type
      },
      body: "HTTP driver test", // If we need to send a body, we can use this. However, this data type must match the "headers"
      responseType: "json",
      onLoad: ({ http }) => {
        console.log(http);

        return {};
      },
      onError: ({ http }) => {
        console.error(http);

        return {};
      }
  }
]}));
```

The above function will log the following in our browser’s console:

![Sample API Response Using Moon's HTTP Driver](https://i1.wp.com/blog.logrocket.com/wp-content/uploads/2020/07/api-response-moon-http-driver.png?resize=730%2C605&ssl=1)<figcaption id="caption-attachment-22097">Sample API response using Moon’s HTTP driver.</figcaption>

Moon’s debugger is also very helpful. If we didn’t define the HTTP driver, it would throw a helpful message on our browser’s console:

![Moon Driver Error Message](https://i1.wp.com/blog.logrocket.com/wp-content/uploads/2020/07/moon-driver-error.png?resize=730%2C403&ssl=1)<figcaption id="caption-attachment-22099">Error when a Moon driver is used without defining it.</figcaption>

Next, we will need to update our `src/main.js` file with the following content:

```jsx
// src/main.js

import Moon from "moon";
import home from "./view/home"

// Define all the drivers that we'll need
Moon.use({
  data: Moon.data.driver,
  http: Moon.http.driver,
  view: Moon.view.driver("#root")
});

Moon.run(() => {
  const postCount = 1 // Fetch the first post initially

  return({
    http: [{
      method: "GET",
      url: `https://jsonplaceholder.typicode.com/posts/${postCount}`,
      headers: {
        "Content-type": "application/json"
      },
      onLoad: ({ http }) => {
        const data = {...JSON.parse(http.body), postCount}
        return {
          view: <home data=data />
        };
      },
    }]
  })}
);
```

The above code will render the `home` component once the API request to fetch the first post is complete.

Now, if we visit [`http://localhost:8080/`](http://localhost:8080/), we should be able to see the following screen:

![Initial Moon Application Interface](https://i2.wp.com/blog.logrocket.com/wp-content/uploads/2020/07/initial-moon-app-interface.png?resize=730%2C408&ssl=1)<figcaption id="caption-attachment-22100">Our initial application interface.</figcaption>

We can view the first post. If we click on the button, the next post will be fetched from the API and rendered on the browser:

![Fetching And Rendering The Next Set Of Posts](https://i2.wp.com/blog.logrocket.com/wp-content/uploads/2020/07/view-with-next-posts.gif?resize=730%2C292&ssl=1)<figcaption id="caption-attachment-22090">Fetching and rendering the next set of posts.</figcaption>

## Conclusion

In this tutorial, we’ve learned what Moon.js is and how we can build an application with it. The source code for the application we built in this tutorial is available on [Github](https://github.com/ghoshnirmalya/moon.js-tailwind-css) and is hosted on [Vercel](https://moon-js-tailwind-css.ghoshnirmalya.vercel.app/).

If you want to keep exploring Moon, check out the [examples](https://moonjs.org/examples). You can also get acquainted with Moon using the Moon [Playground](https://moonjs.org/play/#const%20%7B%20div%2C%20h1%2C%20ul%2C%20li%2C%20input%2C%20button%20%7D%0A%09%3D%20Moon.view.m%3B%0A%0Aconst%20updateTodo%20%3D%20(%7B%20data%2C%20view%20%7D)%20%3D%3E%20%7B%0A%09const%20dataNew%20%3D%20%7B%0A%09%09...data%2C%0A%09%09todo%3A%20view.target.value%0A%09%7D%3B%0A%0A%09return%20%7B%0A%09%09data%3A%20dataNew%2C%0A%09%09view%3A%20%3CviewTodos%20data%3DdataNew%2F%3E%0A%09%7D%3B%0A%7D%3B%0A%0Aconst%20createTodo%20%3D%20(%7B%20data%20%7D)%20%3D%3E%20%7B%0A%09const%20dataNew%20%3D%20%7B%0A%09%09todo%3A%20%22%22%2C%0A%09%09todos%3A%20%5B...data.todos%2C%20data.todo%5D%0A%09%7D%3B%0A%0A%09return%20%7B%0A%09%09data%3A%20dataNew%2C%0A%09%09view%3A%20%3CviewTodos%20data%3DdataNew%2F%3E%0A%09%7D%3B%0A%7D%3B%0A%0Aconst%20removeTodo%20%3D%20index%20%3D%3E%20(%7B%20data%20%7D)%20%3D%3E%20%7B%0A%09const%20dataNew%20%3D%20%7B%0A%09%09...data%2C%0A%09%09todos%3A%20data.todos.filter(%0A%09%09%09(todo%2C%20todoIndex)%20%3D%3E%0A%09%09%09%09todoIndex%20!%3D%3D%20index%0A%09%09)%0A%09%7D%3B%0A%0A%09return%20%7B%0A%09%09data%3A%20dataNew%2C%0A%09%09view%3A%20%3CviewTodos%20data%3DdataNew%2F%3E%0A%09%7D%3B%0A%7D%3B%0A%0Aconst%20viewTodos%20%3D%20(%7B%20data%20%7D)%20%3D%3E%20(%0A%09%3Cdiv%3E%0A%09%09%3Ch1%3ETodos%3C%2Fh1%3E%0A%09%09%3Cinput%0A%09%09%09type%3D%22text%22%0A%09%09%09placeholder%3D%22What%20needs%20to%20be%20done%3F%22%0A%09%09%09value%3Ddata.todo%0A%09%09%09%40input%3DupdateTodo%0A%09%09%2F%3E%0A%09%09%3Cbutton%20%40click%3DcreateTodo%3ECreate%3C%2Fbutton%3E%0A%09%09%3Cul%20children%3D(data.todos.map((todo%2C%20index)%20%3D%3E%0A%09%09%09%3Cli%20%40click%3D(removeTodo(index))%3E%0A%09%09%09%09%7Btodo%7D%0A%09%09%09%3C%2Fli%3E%0A%09%09))%2F%3E%0A%09%3C%2Fdiv%3E%0A)%3B%0A%0AMoon.use(%7B%0A%09data%3A%20Moon.data.driver%2C%0A%09view%3A%20Moon.view.driver(%22%23root%22)%0A%7D)%3B%0A%0AMoon.run(()%20%3D%3E%20%7B%0A%09const%20data%20%3D%20%7B%0A%09%09todo%3A%20%22%22%2C%0A%09%09todos%3A%20%5B%0A%09%09%09%22Learn%20Moon%22%2C%0A%09%09%09%22Take%20a%20nap%22%2C%0A%09%09%09%22Go%20shopping%22%0A%09%09%5D%0A%09%7D%3B%0A%0A%09return%20%7B%0A%09%09data%2C%0A%09%09view%3A%20%3CviewTodos%20data%3Ddata%2F%3E%0A%09%7D%3B%0A%7D)%3B) or [Guide](https://moonjs.org/guide).

* * * 
## [LogRocket:](logrocket.com/devto) Full visibility into production web apps 

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/eq752g8qhbffxt3hp9t4.png)

LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.

[Demo it yourself for free – no account required.](https://logrocket-sample-site.web.app/) 
* * *

The post [Functional user interfaces with Moon.js](https://blog.logrocket.com/functional-uis-moon-js/) appeared first on [LogRocket Blog](https://blog.logrocket.com).