Back to overview

Create a bare bone chat component with Svelte

▼ Post information

Author Jef Meijvis Publish date 01/10/2022
Title Create a bare bone chat component with Svelte Id 5
Source 005-creating-a-chat-component-with-svelte.md Render timestamp Dec 06, 2023, 06:08:30 AM (GMT+1)
Views 1024 Tags Svelte, frontend
Author Jef Meijvis
Publish date 01/10/2022
Title Create a bare bone chat component with Svelte
Id 5
Source 005-creating-a-chat-component-with-svelte.md
Render timestamp Dec 06, 2023, 06:08:30 AM (GMT+1)
Views 1024
Tags Svelte, frontend
opengraph

▼ Table of contents

Share this post:

Frontend with Svelte?

In this article we're going to create a simple chatbox, using Svelte. The design is based on similar chat services, such as Facebook Messenger or WhatsApp. At this point the entire chatbox is local, so no data is going in or out. Below is an image of the finished result:

A chat windows created with Svelte components

Image: A chat windows created with Svelte components

For a live demo you can visit the demo page

Use the 'Username' field to choose a username. It's value is taken as is, so when you pick a name, you will have that identity. This means it's possible to pick an existing user. Whenever you change the current user, the styling of the chatwindow will update, showing which messages are your own. Underneath there is space to type a message, and a button to send the message.

Getting started

Let's start by creating a container div in our main component file (chatbox.svelte). This will be a full page-width div to contain our actual chatbox. Next up, add our actual chatbox div. In here we will create a h1 element to create our title, and another div which we will give the classname message-container. When we apply some styling, we get the following result:

A chat windows created with Svelte components

Image: A chat windows created with Svelte components

Code snippet

1
<div class="container">
2
    <div class="chatbox">
3
        <h1 class="toptext" >Chatbox</h1>
4
        <div class="message-container">
5
        </div>
6
    </div>
7
</div>
8

9
<style>
10
    .message-container
11
    {
12
        overflow-y: scroll;
13
        height : 20rem;
14
    }
15

16
    .toptext
17
    {
18
        background-color: rgb(250, 144, 23);    
19
        color:white;                           
20
        text-align: center;                    
21
        border-top-left-radius: .5rem;        
22
        border-top-right-radius: .5rem;
23
        margin-bottom: 0;
24
    }
25

26
    .chatbox
27
    {
28
        width : 20rem;
29
        margin:auto;
30
        border-radius: .5rem;
31
        box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;
32
    }
33

34
    .container
35
    {
36
        width : 100%;
37
    }
38
</style>

Chat bubble

Let's continue on the frontend by creating a subcomponent, which we will call a message bubble (bubble.svelte). Use the script tag to expose a message prop. Create a p element containing the user that is available on the message object, and the timestamp converted to the local datetime format. We assign the info element a class based by comparing the username of the message with the username of the current user. If these match, it's our own message and we make it a right aligned chat bubble. If not, we make it a left aligned bubble.

Code snippet

1
<script>
2
    export let message; // Message object that contains all information about this specific message
3
    export let user;    // Username of the user that is currently using the chatbox
4
</script>
5

6
<p class="{message.user == user ? 'info-right' : 'info-left'}">{message.user} | {message.timestamp.toLocaleString()}</p>
7

8
<div class="{message.user == user ? 'bubble-right' : 'bubble-left'}">                
9
    <p class="message">{message.message}</p>
10
</div>
11

12

13
<style>
14
    .bubble-left,.bubble-right
15
    {
16
        width : 80%;
17
        margin : .5rem;
18
        border-radius: 1rem;
19
        padding : .25rem;
20
    }
21

22
    .info-left
23
    {
24
        float: left;
25
    }
26

27
    .info-right
28
    {
29
        float: right;
30
    }
31

32
    .info-left,.info-right
33
    {
34
        font-size: .6rem;
35
        margin:0;
36
        padding:0;
37
        margin-left: .7rem;
38
        margin-top: 1rem;
39
        opacity: 50%;
40
    }
41

42
    .bubble-left
43
    {
44
        background-color: rgb(255, 241, 226);
45
        float:left;
46
        border-bottom-left-radius: 0;
47
    }
48

49
    .bubble-right
50
    {
51
        float:right;
52
        background-color: rgb(226, 226, 255);
53
        border-bottom-right-radius: 0;
54
    }
55

56
    .message
57
    {
58
        font-size: .7rem;
59
    }
60
</style>

Before we can continue, we need to create some mockup data to test our chat bubble. Let's create a list of messages, and add these on top of the chatbox.svelte included in a script tag. We also create a field to store the username of the current user.

Code snippet

1
<script>
2
    let messages : any[] = 
3
    [
4
        {id : 1, timestamp : new Date() , user : "Alice" , message : "Hi there, I'm writing a chat message!"},
5
        {id : 2, timestamp : new Date() , user : "Bob" , message : "Hi Alice, what's the weather over there? ☁️"},
6
        {id : 4, timestamp : new Date() , user : "Alice" , message : "It's really sunny over here 🌞😎"},
7
        {id : 3, timestamp : new Date() , user : "Carol" , message : "Hi guys 👋"},
8
    ];
9

10
    let user : string = "Alice";
11
</script>
12

We further update the chatbox.svelte file by using our newly created bubble.svelte file. We want to render this component for every element in the 'messages' list. While doing so, we also provide the message and user object as props to the component.

Code snippet

1
<script>
2
    import Bubble from "./bubble.svelte";
3
</script>
4

5

6
<div class="chatbox">
7
    <h1>Chatbox</h1>
8
    <div bind:this={messageContainer} class="message-container">
9
        {#each messages as message}
10
            <Bubble {message} {user}></Bubble>
11
        {/each}
12
    </div>
13
</div>

By combining all these changes, we get the following result:

A chat windows created with Svelte components

Image: A chat windows created with Svelte components

Looking great so far! We are able to render the message, the user and the timestamp in an interactive chat window. Based on which user we specified in code, we can determine which chat bubbles are ours.

Control panel

Let's finish our chatbox by adding a control panel (controls.svelte). As props to this subcomponent we define a user, a send function and a message. We group everything together in a div with classname controls. The first p element contains a input field that is bound to the 'user' variable. The textarea below is bound to the message variable. Our button contains an on:click event that fires the send method that is provided by the top level chatbox component.

Code snippet

1
<script>
2
    export let user;    // Value of the user input
3
    export let send;    // Function that sends the current message
4
    export let message; // Value of the message input
5
</script>
6

7
<div class="controls">
8
    <p>Username: <input placeholder="Type your username here..." bind:value={user} type=text/></p>
9
    <textarea placeholder="Type your message here..." bind:value={message}/>
10
    <button on:click="{()=>send()}">Send</button>
11
</div>
12

13

14
<style>
15
    input
16
    {
17
        width : calc(100% - .5rem);
18
        border-radius: .25rem;
19
    }
20
    
21
    .controls
22
    {
23
        padding : .5rem;
24
    }
25

26
    p
27
    {
28
        font-size: .75rem;
29
    }
30

31

32
    button
33
    {
34
        width : 100%;
35
        height : 2rem;
36
        background-color: rgb(250, 144, 23);
37
        color:white; /**/
38
        cursor:pointer;
39
        border-radius: .25rem;
40
        border:none;
41
        transition: all ease .25s;
42
    }
43

44
    button:hover
45
    {
46
        background-color: rgb(255, 203, 144);
47
    }
48

49
    textarea
50
    {
51
        height : 5rem;
52
        width : calc(100% - .5rem);
53
        margin:0;
54
        padding:0;
55
        resize:none;
56
        border-radius: .25rem;
57
        padding : .25rem;
58
    }
59
</style>

A few more things are needed back in our chatbox.svelte component to integrate the controls:

First let's add an import statement for our controls component. We also need a variable (messageContainer) to bind to the message-container div. We define a send function, which adds the current message to the array of messages. We do include a few checks to only send the message when both a username and message is provided.

Note

Svelte only re-renders it's components when the variable is changed. This means that when we have a component that uses an array, adding or removing an element from the list will not trigger a re-render. We can solve this be reassigning the variable storing the list to itself: messages = messages;

To improve the user experience, the chatbox will scroll to the bottom when a new message is added. We can achieve this by triggering our custom 'scrollToBottom' function whenever the afterUpdate hook fires.

Finish things of by including the Controls component in our chatbox div, while binding it to the message, user and send props.

Code snippet

1
<script lang="ts">
2
    import { afterUpdate } from "svelte";
3
    import Bubble from "./bubble.svelte";
4
    import Controls from "./controls.svelte";
5

6
    let messageContainer;
7

8
    let messages : any[] = 
9
    [
10
        {id : 1, timestamp : new Date() , user : "Alice" , message : "Hi there, I'm writing a chat message!", likes : 0 },
11
        {id : 2, timestamp : new Date() , user : "Bob" , message : "Hi Alice, what's the weather over there? ☁️", likes : 0 },
12
        {id : 4, timestamp : new Date() , user : "Alice" , message : "It's really sunny over here 🌞😎", likes : 0 },
13
        {id : 3, timestamp : new Date() , user : "Carol" , message : "Hi guys 👋", likes : 0 },
14
    ]
15

16
    let user : string = "Alice";
17
    let message : string;
18

19
    afterUpdate(() => {scrollToBottom(messageContainer)});
20

21
    async function send()
22
    {
23
        if(!message || message == undefined || message == "" || !user || user == undefined || user == "")
24
            return;
25

26
        messages.push({timestamp : new Date() , user : user , message : message, likes : 0, id : messages.length+1});
27
        message = ""; 
28
        messages = messages;
29
    }
30

31
    const scrollToBottom = async (node : any) => {node.scroll({ top: node.scrollHeight, behavior: 'smooth'});}; 
32
</script>
33

34

35
<div class="container">
36
    <div class="chatbox">
37
        <h1>Chatbox</h1>
38
        <div bind:this={messageContainer} class="message-container">
39
            {#each messages as message}
40
                <Bubble {message} {user}></Bubble>
41
            {/each}
42
        </div>
43
        <Controls bind:message={message} bind:user={user} {send}></Controls>
44
    </div>
45
</div>

This gives us the following result, as showed at the start of the article:

A chat windows created with Svelte components

Image: A chat windows created with Svelte components

Back to top