Build your own live chat web component with Ably and AWS
Published: 2021-07-16 13:45:03

Web Components are a great way to build reusable functionality you can use in different web pages and web apps. Imagine sharing components between different frameworks like React, Vue.js, or Next.js! In this post, we delve into Web Components and show you how to build a chat web component with Ably to use it in an application built with AWS Amplify and AWS Lambda.

## What is a Web Component?

 Web Components are a collection of web technologies, standardized in the browser, to ease writing reusable pieces of markup and functionality. They’re a combination of <a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements">Custom Elements</a>, the <a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM">Shadow DOM</a> and <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLTemplateElement">HTML templates</a>. When they’re used all together, they’re collectively called <a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components">Web Components</a>.
 Web Components are an alternative to framework-specific approaches to component reuse such as React Components, and Vue.js templates. Web Components are interesting because they’re not bound to any runtime framework, and they’re supported by all modern browsers. Competing approaches to components for the web were established prior to Web Components being widely supported, which caused a lot of the frameworks to develop their own approaches to reuse and encapsulation. Web components offer a framework-independent way of building reusable functionality for the browser.

## The basics of a Web Component

 When talking about Web Components, most people refer to Custom HTML Elements, as these are the closest thing to the component models found in React or Vue. Custom Elements can:

* Render HTML into themselves by setting their own innerHTML properties.
* Encapsulate data by storing it as attributes on instances of themselves.
* Track changes to data stored in their attributes, and trigger a re-render.
* Allow you to write code that is triggered when they are added and removed from the DOM.

## Writing a basic web component

 The first thing we'll do in this post is create an example of a Custom Element that displays the number of times a button has been clicked. To create this Custom Element, we need to create a new JavaScript file that we can reference from our web page. Start by creating a new file called *index.js*. We'll define a class for the Custom Element and call it `CountComponent`. This will extend HTMLElement (the browser's base type for all its elements):

{% gist https://gist.github.com/thisisjofrank/641e723ad1bc2c4ee80588ce15de9047 %}

Inside this class, we need to define the attributes to be observed for `change tracking`. For the sake of this demo, we will return an array of a single string – the attribute name `count`:

{% gist https://gist.github.com/thisisjofrank/f4254f473f6ede4d82ba14bae37b580a %}

Next, we need to define some custom attributes and their behaviour. We create a `getter`for the custom “count” attribute, and in the function, the attribute value `count` is loaded with a call to `getAttribute`, defaulting to the *string* `"0"`. Once the data is loaded, we’re parsing it from JSON – we do this because any data that we store must be a string.

{% gist https://gist.github.com/thisisjofrank/b935cb282e93cb0faec44a09a645c974 %}

 We also need to create a *setter* for our attribute, and here we’re serializing the value to JSON and setting it on our element:

{% gist https://gist.github.com/thisisjofrank/33866f3fd1bf8028140a710b3b20ef68 %}

Even though Custom Elements are defined as classes, you can’t put any logic inside their constructors other than calling `super` to trigger the base logic for the HTML Element class. *Add any other logic, or miss out calling super and you’ll see an error in your console and the element won’t work*.

{% gist https://gist.github.com/thisisjofrank/19cff94d50b2c23e53c385d0b8d73c98 %}

Because we can’t do any meaningful work in the constructor, Custom Elements provide <a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements#using_the_lifecycle_callbacks">lifecycle callbacks</a> – functions that you can implement at different parts of the component lifecycle. We will use the `connectedCallback` function. Implementing this callback will cause the code to run when the component is added to the DOM – it is similar to React's `componentDidMount` function. In connectedCallback, we’re setting a default for the counter, and calling a function we’ll look at shortly called `setContent`:

{% gist https://gist.github.com/thisisjofrank/2ef4ea404c1b450588799bd956f2af6e %}

The next lifecycle callback function available to us is `disconnectedCallback` (shown here only for illustration purposes as we’re not running any code). The code in this function executes when your element is removed from the DOM and destroyed.

{% gist https://gist.github.com/thisisjofrank/69b1cd694481b2f6420a57795caac7dd %}

Custom Elements also provide us with an `attributeChangedCallback`. We will use this function to execute code whenever an `observedAttribute` changes:

{% gist https://gist.github.com/thisisjofrank/099126e5f7f53506575fc3523996a5c6 %}

`setContent` is a function that the component calls when the component is added to the DOM, and when an update happens. The click handler in the above code can access the attributes of the element using the `this` keyword, so on each click, we increment the value, in turn triggering a re-render.
*Note: This demo is not particularly performance focused, in that it sets the entire innerHTML and wires up a click handler to the button each time it’s called. This is not how one would build an application for production! In a real application, you would not reset your entire DOM. Instead you would only change the parts of the UI that required updating. Furthermore, the <a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM">Shadow DOM APIs</a> could be used in more performance-sensitive scenarios.*

Finally, at the end of the JavaScript file, we call the browser's <a href="https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry/define">customElements.define API</a> – providing a tag name for the element, and a reference to the class we just defined.

{% gist https://gist.github.com/thisisjofrank/22ebbd38cbede41511c48b5987680a83 %}

## Using the basic web component

It is now possible to reference the Custom Element JavaScript file as a module and add the element we just made to the page using the tag name that we defined:

{% gist https://gist.github.com/thisisjofrank/09f20cdf17d0121680e6c5c97c742704 %}

And the component renders thus

![screenshot of rendered component](https://ik.imagekit.io/ably/ghost/prod/2021/07/counter-3.png?tr=w-1520)

## Building a Chat web component with Ably

We're going to build a text chat component – Ably Chat component – by building *two* Custom Elements:

* AblyBaseComponent.js – a base component that encapsulates Ably interactions
* AblyChatComponent.js – an element that inherits from AblyBaseComponent and implements chat logic on top of Ably.

The base component is designed strictly to be built upon, and to encapsulate the basic requirements for Ably API key management and subscribing to channels. Using this base component, we could build any kind of Custom Element that relies on realtime messaging.

In order to connect to Ably, you need an <a href="https://ably.com/signup">Ably account</a> and an<a href="https://knowledge.ably.com/setting-up-and-managing-api-keys"> API key</a>.

### Building the Base Component

The base component starts with the standard Custom Element boilerplate – AblyBaseComponent extends HTMLElement, and calls its constructor.

{% gist https://gist.github.com/thisisjofrank/6ec835c28742e91c77ef9fd975471eaa %}

In the connectedCallback function, we call `this.connectToAblyChannel` (which we’ll examine shortly):

{% gist https://gist.github.com/thisisjofrank/75962469b8c2bdb8421f298ec01747ed %}

The disconnectedCallback function loops through `this.subscribedChannels` (which we will create in a `connect` function) and unsubscribes from any Ably channels that have been used:

{% gist https://gist.github.com/thisisjofrank/205eb33d3b54c15be7faead5a1942a25 %}

The `connectToAblyChannel` function is where a lot of the real work happens. We start off by loading some configuration – we will set up a convention that we expect users to supply *either* their Ably API key, or a callback URL to an API that responds with an Ably token authentication request. To supply these values, and because we’re creating HTML elements, **we expect them to be set as attributes on the element when it’s created in the markup**.
 
In order to read the data attribute when the element is connected, we expect the element to look as follows:

{% gist https://gist.github.com/thisisjofrank/6710a6ea39f19c05e57596b3a025ae07 %}

Next we need to design a way to configure the Ably client. In regular JavaScript you might use the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Document">Document API</a> to interact with elements in the DOM – but because we’re literally <em>inside </em>the custom element, we can use DOM API calls like <a href="https://www.w3schools.com/jsref/met_element_getattribute.asp">getAttribute</a> from the `this` object.

{% gist https://gist.github.com/thisisjofrank/05dfea67a2aa34ffd0ec9264fbac10ae %}

This code tries to load `data-api-key` and `data-get-token-url`. If an API key is found, it takes precedence, otherwise, we create an Ably JavaScript SDK configuration object that contains an “authUrl” property. Now that we have a configuration object (either an API key or a URL), we can create an instance of the Ably Realtime JavaScript SDK. *It is important to point out that this Custom Element relies on the containing page having already included the Ably JavaScript SDK using a script element before it is executed.* The following code comes after the configuration, inside the `connectToAblyChannel` function:

{% gist https://gist.github.com/thisisjofrank/f8a3778af376fec990827d2dbc1b7810 %}

Once we have the SDK instance, stored in the variable `this.ably`, we will also create an empty array called `subscribedChannels`.

We’re going to conclude with two additional functions in the AblyBaseComponent – a `publish` function and a `subscribe` function.

The regular Ably SDK exposes publish and subscribe functions once you have called `ably.channels.get(channelName)`to get a channel. For this chat example, we want to let the AblyChatComponent decide which channels it will publish and subscribe on, effectively extending the API surface area of the Ably SDK (the set of things the API can do).

The augmented publish and subscribe functions take an extra parameter at the start – the channel name – then pass on the rest of the arguments to the Ably SDK to do all the hard work for us. We do this by destructuring the *arguments* array, and ignoring the first element. This allows us to capture all of the parameters, except the first one, in our newly defined variable `args`:

{% gist https://gist.github.com/thisisjofrank/2a13c903037307c0890d0f4af6ec2182 %}

We can then use `.apply` on the publish or subscribe calls from the Ably SDK to pass the rest of the variables to the SDK to publish or subscribe to messages.

We will use the first parameter, the `channelName`, to get the correct channel and keep track of it so we can unsubscribe when we unmount from the DOM. The following code will go inside the `publish` function, below the arguments assignment. The subscribe function works similarly

{% gist https://gist.github.com/thisisjofrank/f55c6b4b3b06b858e60b7df6a0d3c75d %}

As you can see, both `publish` and `subscribe` are virtually identical functions – with the exception that we’re passing to them the call to `channel.publish` or `channel.subscribe`, respectively.

This all might seem a little framework-like, but what it means is that developers building Ably components on top of this base class can call `publish` or `subscribe` with an extra `channelName` parameter at the start of the call, and not worry about connecting or disconnecting from Ably channels.

This base class contains all our Ably code, and is now ready for us to build exciting components on top of it, so let’s create our second component, the **AblyChatComponent**.

### Building the Chat Component

These components are designed to be imported as an ES6 Module – where the script tag you use in your HTML has `type="module"`as an attribute. When using ES6 modules, we can use `import` in browser components, so to start here, we’re importing our AblyBaseComponent in order to extend it.

{% gist https://gist.github.com/thisisjofrank/82b096f34e782de8a6105f9017dd8c55 %}

The next thing to do is set up some boilerplate code for the element. Define an attribute called “messages”, and set it up to be observable. Next, set up `constructor`, which in turn calls the constructor of the **AblyBaseComponent** using a call to `super();`.

{% gist https://gist.github.com/thisisjofrank/ca42d7208fc1bf0d5003617fa1163415 %}

We can use `connectedCallback` to configure the chat application. We call `super.connectedCallback` to trigger all of the Ably configuration logic from the base component. The following code goes below `constructor`, inside the closing bracket of the `AblyChat` class:

{% gist https://gist.github.com/thisisjofrank/93b8e5eeb907fb4a2e8ae9e4f159bfea %}

We call the `super.subscribe* *`function that was defined on the base to subscribe to messages on a channel called `chat`. The rest of the parameters, as we pointed out earlier, are standard Ably JavaScript SDK parameters – we’re subscribing only to messages on the topic `chat-message`. When a message is received, we call a function called `this.onAblyMessageReceived` (which we’ll implement in a moment), passing the received message as an argument.

In order to ensure that any CSS styles applied to the page don't affect the component, and vice versa, inside the body of `connectedCallback`, we will generate a random string and assign it to a property called `id`:

{% gist https://gist.github.com/thisisjofrank/9fe2dcd4aa702933bf8255f6ef4fdbe6 %}

Next we call a function named `renderTemplateAndRegisterClickHandlers`, which we’ll look at shortly.

{% gist https://gist.github.com/thisisjofrank/6b90a0270dc3053604149e05e3fcb652 %}

Finally, we place the browser focus on an element called `this.inputBox` that is generated when the template is rendered, so that people using the chat UI will be able to instantly start typing.

{% gist https://gist.github.com/thisisjofrank/320341a8e16d23713c625dcd1c7576c6 %}

Next we use the `attributeChangedCallback` to update the innerHTML of the chat bubbles in the chat window when messages are received. When an attribute is changed, the `innerHTML` of `this.chatText` is set and scrolled into view. We’re using a function called `formatMessages`, which takes the message history, and converts it to HTML elements fit for display:

{% gist https://gist.github.com/thisisjofrank/b6b72d8dc4b691222d48ed5c4d1034bf %}

Next we set up the `renderTemplateAndRegisterClickHandlers` function, which is named for what it does! The start of this function calls another function called `defaultMarkup`, that takes a single parameter – the *id *of the element, and returns the innerHTML we want to display on the screen – an empty chat box element.

Once the element has been rendered into the DOM, we can use `querySelectorAll` to find the chatText, inputBox, sendButton, and messageEnd elements so that we can use them in our code:

{% gist https://gist.github.com/thisisjofrank/32a1e473bf2e65462d39e2a5b9738bc3 %}

Inside we also wire up the eventListeners for clicks on the sendButton and on each keyPress in the input box so that we can process user input.

{% gist https://gist.github.com/thisisjofrank/67082140da318a91d2ab4f22ccc553ca %}

We mentioned the `onAblyMessageReceived` function earlier when we subscribed to Ably messages – this function takes the current message history from `this.messages`, makes sure it is at most 199 messages long, and adds the latest message to the end of the array:

{% gist https://gist.github.com/thisisjofrank/ee427db2c68a69d6aea4cefd6f663760 %}

This new array is then assigned to `this.messages`, which triggers the UI to re-render because `this.messages` is an observed property.

The `sendChatMessage` function is called either when Enter is pressed, or when the button to send a message is clicked. Because we’re extending the AblyBaseComponent, it calls the `super.publish` function, passing the channel name and the Ably message payload:

{% gist https://gist.github.com/thisisjofrank/a90ebb4ba4cc7976961b5b0f1c81c5a1 %}

You can see that it’s also responsible for clearing the text box and focusing on it so the user can continue chatting.

The `handleKeyPress` function is triggered on each keypress. If the keypress is the Enter key *and* there is a message in the chat box, it sends the chat message:

{% gist https://gist.github.com/thisisjofrank/9ee54bf02bac37d83d8e1fd65cec152e %}

The `formatMessages` function is responsible for mapping the Ably message history into span elements. There’s a little bit of logic here to detect whether or not the message was sent by the current user of the app by checking the `message.connectionId` property against the `ably.connection.id` property, and adding a `me` or `other` CSS class that can apply styles to. The `data` property from the message is used to carry the received text message.

{% gist https://gist.github.com/thisisjofrank/068b039ab4e8d783415c7c5e70092993 %}

The above concludes the custom element class. After the custom element class ends, we have two functions. The first is the `uuidv4()` function – which generates a unique `id` for the component:

{% gist https://gist.github.com/thisisjofrank/1ebd732c6e6643ffc6496c4a577bb419 %}

The second is `defaultMarkup` which takes a single parameter – the ID of the component – and uses it to set the `id` property on the generated HTML.

Once we’ve set this `id` property, we can embed CSS that is scoped specifically to this element ID directly into the output code. This means that if multiple instances of the custom element appear on the same page, they won’t have conflicting *ids* or *styles*.

At the bottom of this next snippet you can see the markup for the component – a `div` for holding message history, and a `form` for capturing user input, complete with the *class names* used in our *query selector calls* earlier. 

{% gist https://gist.github.com/thisisjofrank/78791826971ab7345debc466290f3665 %}

And of course, much like our example element at the start, we’re calling `customElements.define` to register the HTML tag: 

{% gist https://gist.github.com/thisisjofrank/227cbe177cde6d91be174f74dfd98c3b %}

The custom element is now functionally complete, and so long as both the AblyBaseComponent.js and AblyChatComponent.js files are included in a web application, they can be used by referencing our AblyChatComponent.js as a module.

{% gist https://gist.github.com/thisisjofrank/818d5a4ff1612c8ade717ce9a52c02e7 %}

{% gist https://gist.github.com/thisisjofrank/f3279900be6c99a9cc3af3845a79c1a8 %}

## Using the Chat Component

To use the now-registered custom element, we use it like any old HTML tag and provide it with the correct attributes:

{% gist https://gist.github.com/thisisjofrank/38eeae4b219316ef78632f7dc414fdae %}

The element renders like this in the page, and when configured with an API key or get-token-url, it just works!
![screenshot of finished web component](https://lh3.googleusercontent.com/uwCOKwbTPcG4wGVQf4tOkaoo7vUafvpDRdQLPLoQZyMfVZjYJHdCsqvx1pchii65WEKX-XWz6s_fNamj29iHCkBxpebLRyHV5uU7btXlVZCNq-CD31H5i4l9oqYJ1grUb12bZwtk)

## API Key Management

As hinted above, we need to talk about API key management. While this custom element supports reading Ably API keys straight from your markup – which is great for local development and debugging – you *absolutely should not* store your Ably API keys in your markup, otherwise they could get stolen and misused.

The recommended way to use API keys in the front-end is to use <a href="https://ably.com/documentation/core-features/authentication/#token-authentication">Ably Token Authentication</a>. Token authentication is an exchange mechanism where you use your real API key to generate limited-use tokens that can be passed back to your clients to use in the front end.

In order to use token authentication in this way, we need to create an API somewhere, to call from the front-end, that has your real Ably API key stored in it. We can then use a function in the Ably SDK to exchange your real API key for a token that gets returned to the Ably JavaScript SDK. The <a href="https://github.com/ably/ably-js">Ably JavaScript SDK</a> manages this token exchange process for you. When you provide a URL that points to an API that will return a token, the SDK will manage and refresh the token as required, so you don’t need to worry about it. This demo will walk through using AWS Lambda functions and the AWS API Gateway to achieve this.

## Serverless Token exchange with AWS Lambda

The following example AWS Lambda function provides the necessary token exchange functionality. All we need to do is require the Ably JavaScript SDK, and create an instance of the `Ably.Realtime` client passing your Ably API key from *process.env.ABLY_API_KEY.*

{% gist https://gist.github.com/thisisjofrank/3c718c00e378ba796592e412b886361a %}

We use `client.auth.createTokenRequest` to generate a temporary token, and return it back to the client.

The onus is on the owner of the API key to make sure that the users requesting a temporary token have access to chat – you can authenticate the requests any way you’d like, and it’s no different from authentication in any other lambda function. In the next section, we go through hosting this on AWS Lambda

## Using AWS Lambda for Authentication

To deploy to AWS Lambda, we need to create a new directory called /api/createTokenRequest with two files in it – package.json and index.js Here’s the package.json file


{% gist https://gist.github.com/thisisjofrank/a7a5115d1d1bb929935393156b62b11c %}

And this is the index.js file

{% gist https://gist.github.com/thisisjofrank/d3f2593420da9b149537d8881b53f6a2 %}

These two files, along with their node_modules are required by the AWS Lambda runtime. We're going to use npm to restore the node modules and then compress the contents of the /createTokenRequest directory into a zip file. In the terminal execute the following:

{% gist https://gist.github.com/thisisjofrank/2a20926e0c761fab1810cb99109e8421 %}

After that, zip up the contents of the createTokenRequest directory (this process is OS dependent). We will use the AWS UI to create a Lambda function and upload this zip file as the source code.

We'll walk through this process now. You need to <a href="https://aws.amazon.com/free/">log in to your AWS account</a> first:

1.  Search for lambda in the Services search bar, and click the Lambda services box that shows up in the results: ![lambda1-2](https://ik.imagekit.io/ably/ghost/prod/2021/07/lambda1-2.png?tr=w-1520)
2.  Click the “Create function” button to create a new Lambda function ![lambda2](https://ik.imagekit.io/ably/ghost/prod/2021/07/lambda2.png?tr=w-1520)
3.  Select "Author from scratch" and give your function a name, then click "Create function": ![lambda3](https://ik.imagekit.io/ably/ghost/prod/2021/07/lambda3.png?tr=w-1520)
4.  Once the function has been created, click “Add trigger” to make the Lambda function accessible over HTTP: ![](https://ik.imagekit.io/ably/ghost/prod/2021/07/lambda4_needspixelating.png?tr=w-1520)
5.  Select “API Gateway” from the “Trigger configuration” dropdown ![lambda5](https://ik.imagekit.io/ably/ghost/prod/2021/07/lambda5.png?tr=w-1520)
6.  On the resulting page, select your function from the dropdown and click "Add". ![lambda6](https://ik.imagekit.io/ably/ghost/prod/2021/07/lambda6.png?tr=w-1520)
7.  Set the Deployment stage to default and the Security to Open, then click "Add". ![lambda7](https://ik.imagekit.io/ably/ghost/prod/2021/07/lambda7.png?tr=w-1520)
8.  Once you have added your trigger, the UI shows a URL in the Triggers tab under Configuration. (This is what you will add as the `data-get-token-url` parameter when you use your component in the HTML, but we still have some more setup to do!) ![lambda13](https://ik.imagekit.io/ably/ghost/prod/2021/07/lambda13.png?tr=w-1520)
9.  Now you need to upload the zip file that we created earlier. Click on the "Code" tab, then "Upload from" and select ".zip file": ![lambda10-1](https://ik.imagekit.io/ably/ghost/prod/2021/07/lambda10-1.png?tr=w-1520)
10.  Once the zip file is uploaded, you will need to set up your environment variables with your Ably API key. Under "Configuration", select "Environment variables", then click the "Edit" button: ![lambda11](https://ik.imagekit.io/ably/ghost/prod/2021/07/lambda11.png?tr=w-1520)
Add your Ably API key to the “Environment variables” settings ![lambda12](https://ik.imagekit.io/ably/ghost/prod/2021/07/lambda12.png?tr=w-1520) And that's it, your Lambda is set up

And that's it, your Lambda is set up

In the index.js file we’re reading from `process.env.ABLY_API_KEY`. You will need to <a href="https://knowledge.ably.com/setting-up-and-managing-api-keys">generate a new ably API key</a> and then define this environment variable, with it’s key value in the AWS UI (or using an automation tool of your preference).

Once our Lambda function is created, we’ll need to add an AWS API Gateway Trigger to give our lambda an externally accessible URL. This is the URL that we can safely configure in our HTML markup in lieu of our actual API key. The Ably SDK will take care of the rest.

{% gist https://gist.github.com/thisisjofrank/0d7d8a839db7acabc1ccb9c349f414d2 %}

## Hosting your Web Component on Amplify

Now we can walk through hosting your component on Amplify, the <a href="https://aws.amazon.com/amplify/">AWS static web hosting service</a>.

1.  Search for Amplify in the Services search bar and click on the resulting AWS Amplify link.  
    ![step1](https://files.ably.io/ghost/prod/2021/07/step1.png)
    
2.  Click "GET STARTED"  
    ![step2](https://files.ably.io/ghost/prod/2021/07/step2.png)
    
3.  Scroll down the resulting page to "Host your web app, and click on "Get started":  
    ![step3](https://files.ably.io/ghost/prod/2021/07/step3.png)
    
4.  Authenticate AWS Amplify with your GitHub account  
    ![step4](https://files.ably.io/ghost/prod/2021/07/step4.png)
    
5.  Select the repository of your web component  
    ![step5](https://files.ably.io/ghost/prod/2021/07/step5.png)
    
6.  Edit your build settings to include `npm run ci` as a prebuild command and set the baseDirectory to "/build".  
    ![step6](https://files.ably.io/ghost/prod/2021/07/step6.png)
    
7.  Click the "Save and deploy" button to host your component  
    ![step7](https://files.ably.io/ghost/prod/2021/07/step7.png)
    
8.  If all is well, the component will provision, build, and deploy successfully. The UI will provide you with a URL to view your hosted component.  
    ![step8](https://files.ably.io/ghost/prod/2021/07/step8.png)
    

The hosted web component will look something like this  
![web-component](https://files.ably.io/ghost/prod/2021/07/web-component.png)

## Using the NPM package
![npm logo](https://ik.imagekit.io/ably/ghost/prod/2021/07/npm.png?tr=w-1520)
Because components are built as plain old JavaScript, we can distribute and consume the component using NPM and any of the variety of browser-friendly ways to add NPM packages to your front end.

Here at Ably, we’ve <a href="https://www.npmjs.com/package/ably-chat-component">published the component to NPM</a> as ably-chat-component, and you can reference it directly using the *Skypack* CDN. This ensures packages are browser-compatible.


{% gist https://gist.github.com/thisisjofrank/3d6126d24b85ff4456c5134726be58e1 %}

You need to reference the client-side Ably SDK for the component to work. However, once you have done that, you can reference the Skypack URL for our component, and add the ably-chat tag into your page, set your API key, and everything will just work.

This is the simplest supported way to use this component in development mode. As mentioned above, however, you will need to *switch out your API key* for a token request URL of your own.

## Component Architecture

Diagrammatically, the component architecture can be depicted as such:
![architecture digagram](https://ik.imagekit.io/ably/ghost/prod/2021/07/web-component-chat-app-architecture@2x.png?tr=w-1520)

With the corresponding sequence diagram
![sequence diagram](https://ik.imagekit.io/ably/ghost/prod/2021/07/web-component-chat-app-sequence@2x--1-.png?tr=w-1520)

## Conclusion

In this piece we’ve broken down how web components work, explored Ably and Web Components, and walked through how we can use AWS Amplify and AWS Lambda to host applications that support realtime chat.

If you’ve already got a web application and know how to host it, we’ve also touched on how you can use Skypack to <a href="https://www.npmjs.com/package/ably-chat-component">include this component directly from NPM</a>.

Chat is just one way that you can use realtime messaging and Web Components, and we’d love to see what you can do with this codebase.