# Usage with TMI.js

We designed emoteTTV to work seamlessly with TMI.js, for that reason you can use your tags directly into our parsers. In this guide, we'll use vanilla JavaScript to create a fully-fledged chatbox in no time.

## Introduction

For starters, let's consider the following TMI.js implementation:

```typescript
import tmi from "tmi.js";

const client = new tmi.Client({
    channels: [ "doceazedo911" ]
});

client.connect();
client.on("message", (channel, tags, text, self) => {
    console.log({channel, tags, text, self});
});
```

At this point, we can see that `text` is yet to be parsed and that `tags` hold some very valuable information for us such as emote offsets and badges information.

So when we receive a message let's call emoteTTV and parse the emotes and badges:

```typescript
// ...
import { parseBadges, parseEmotes } from "emotettv";

const options = {
    channelId: "98776633"
};

client.on("message", async (channel, tags, text, self) => {
    const badges = await parseBadges(tags.badges, tags.username, options);
    const badgesHTML = badges.toHTML();
    const message = await parseEmotes(text, tags.emotes, options);
    const messageHTML = message.toHTML();
    console.log({badgesHTML, messageHTML});
});
```

{% hint style="info" %}
Note that `parseBadges()` and `parseEmotes()` are async functions, that way the message handler on line 8 should also be async.
{% endhint %}

Great! Now we have parsed the emotes and badges and converted the results to HTML. It might be a bit clunky to read HTML in the console, so let's grab a few more tags from TMI.js to render this message on the document body instead:

{% code fullWidth="false" %}

```typescript
// ...
const name = tags["display-name"];
const color = tags.color;
document.body.innerHTML = `${badgesHTML} <b style="color:${color}">${name}</b>: ${messageHTML}`;
```

{% endcode %}

Now, when a message is sent you should see something like this:

<figure><img src="/files/6l6zkCbqg1QOlBpm3TYq" alt="" width="375"><figcaption></figcaption></figure>

## Building a chatbox

As we are rendering the message on the document body, if we get another one, it will replace the current. There are many ways to show multiple messages on the screen like a chatbox, in this example, let's make a list of rendered messages, push the new ones and limit it to the last 20 items. Finally, we join all the messages and render them on the body:

```typescript
let messages = [];

client.on("message", async (channel, tags, text, self) => {
    // ...
    const newMessage = `<div>${badgesHTML} <b style="color:${color}">${name}</b>: ${messageHTML}</div>`;
    messages = [...messages, newMessage].slice(-20);
    document.body.innerHTML = messages.join("");
});
```

What might happen now is that as messages come, they might overflow and the new messages might start rendering off the screen. To fix this, we can scroll to the bottom of the page after rendering the messages:

```typescript
// ...
document.body.innerHTML = messages.join("");
window.scrollTo(0, document.body.scrollHeight);
```

## Wrapping up

In the end, your code should look something like this:

```typescript
import tmi from "tmi.js";
import { parseBadges, parseEmotes } from "emotettv";

const client = new tmi.Client({
    channels: [ 'doceazedo911' ]
});

const options = {
    channelId: "98776633"
};

let messages = [];

client.connect();
client.on("message", (channel, tags, text, self) => {
    const badges = await parseBadges(tags.badges, tags.username, options);
    const badgesHTML = badges.toHTML();
    const message = await parseEmotes(text, tags.emotes, options);
    const messageHTML = message.toHTML();
    const newMessage = `<div>${badgesHTML} <b style="color:${color}">${name}</b>: ${messageHTML}</div>`;
    messages = [
      ...messages,
      newMessage
    ].slice(-20);
    document.body.innerHTML = messages.join("");
    window.scrollTo(0, document.body.scrollHeight);
});
```

And as messages come, they should be rendered like this:

<figure><img src="/files/WuXXGTQ6QAajAHhv1nBW" alt="" width="359"><figcaption></figcaption></figure>

Keep in mind this is using all the defaults. You could disable inline styles and add your own CSS to the page, add animations, disable HTML escaping and let your chatters write code on your screen (with caution) or anything you want, really!

Rendering user input should always be done with caution, and the more you reuse the same elements and styles, using a frontend framework might come in handy. If that interests you, you should take a look on [how to use emoteTTV with Svelte](/emotettv/usage/usage-with-svelte.md).


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://emotettv.gitbook.io/emotettv/usage/usage-with-tmi.js.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
