Building a working chat bot in Framer X.
Framer X is an interaction design tool for building React powered interfaces. Learn how to build a chat bot in this tutorial.
Start fresh
First, open a new fresh Framer X project file and open the Components tab. From here we will need three separate code components, a chat bubble, a message input and a chat window.
The Chat Bubble
- Create a new Code component and name it
ChatBubble
. - You can leave the two import statements, but delete the rest of the code inside the generated code file.
- Follow the imports with our class constructor.
- Use two divs with our message nested inside to control our bubble sizing.
import * as React from "react";
import { PropertyControls, ControlType } from "framer";export class ChatBubble extends React.Component<Props> {
render() {
return (
<div>
<div>Chat Message</div>
</div>
);
}
}
Let’s add two CSS styles after our render()
function. Then assign them to their respective divs.
import * as React from "react";
import { PropertyControls, ControlType } from "framer";export class ChatBubble extends React.Component<Props> {
render() {
const containerStyle: React.CSSProperties = {}
const bubbleStyle: React.CSSProperties = {}
return (
<div style={containerStyle}>
<div style={bubbleStyle}>Chat Message</div>
</div>
);
}
}
Now fill the styles to give our ChatBubble
component some shape. We needed two divs in order to let the inner bubble take its size based on the text inside of it. To do this we will tell the container to base its size on the component while the bubble will have a width and height of "auto"
. Let’s also give the bubble a temporary background
color and make sure to set its display
to inline-block
so the container knows to hug its text.
To grab the components width, use this.props.width
, same goes for the height. Also give the container some flex styling, we’ll need to set the flex direction to control which side the bubble should display on.
...const containerStyle: React.CSSProperties = {
width: this.props.width,
height: this.props.height,
// Flex Styles
display: "flex",
flexDirection: "row",
justifyContent: "flex-start",
alignItems: "flex-start",
}const bubbleStyle: React.CSSProperties = {
width: "auto",
height: "auto",
background: "pink",
display: "inline-block",
}...
To complete this ChatBubble
component we will add some property controls that expose the background color, text color and message inside the bubble. We will also use a toggle to flip the bubble from the left to the right. First let’s state our interface Prop
under the import
statements, then we add the Framer X property control options before our render()
function.
import * as React from "react";
import { PropertyControls, ControlType } from "framer";interface Props {
width: number;
height: number;
message: string;
background: string;
color: string;
side: "left" | "right";
}export class ChatBubble extends React.Component<Props> { static defaultProps = {
message: "How are you?",
background: "#F1EFF6",
color: "#333333",
side: "left",
} static propertyControls: PropertyControls = {
message: { type: ControlType.String, title: "Message" },
background: { type: ControlType.Color, title: "Background" },
color: { type: ControlType.Color, title: "Text" },
side: {
type: ControlType.SegmentedEnum,
options: ["left", "right"],
title: "Side"
}
}render()...
Now we can attach the property names to our styles in order to control them, add some extra styling if you desire. We also need to replace the string inside our bubble div to {this.props.message}
so its hooked in sync with our message
prop.
...render() { const containerStyle: React.CSSProperties = {
width: this.props.width,
height: this.props.height,
// Flex Styles
display: "flex",
flexDirection: this.props.side === "left" ? "row" : "row-reverse",
justifyContent: "flex-start",
alignItems: "flex-start",
} const bubbleStyle: React.CSSProperties = {
width: "auto",
height: "auto",
background: this.props.background,
color: this.props.color,
padding: "6px 9px",
marginBottom: "6px",
borderRadius: "6px",
display: "inline-block",
font: "500 14px/18px SF Pro Text",
letterSpacing: "-0.2px",
} return (
<div style={containerStyle}>
<div style={bubbleStyle}>{this.props.message}</div>
</div>
);
}...
And that will complete our ChatBubble
component. As you can see we simply control the bubble direction with this simple conditional statement
flexDirection: this.props.side === "left" ? "row" : "row-reverse"
Message Input
We know for our bot that the user should be able to enter a message and hit “Enter” to send it to the chat window. Then, the bot will reply with a new message in the chat window.
Lets create an input that listens for the “Enter” key being pressed and pass that event along to its parent—which in this project is the chat window.
To save time let’s take some search bar code already provided for us in the Framer X Store.
- First open the Store tab.
- Search for the Example Kit provided by the Framer team.
- Install the package and head back to your Components tab.
- You should now see a list of components under the Example Kit title bar. Look for the
SearchBar
component and right-click, and copy the code.
Instead of just using the provided search bar we will create our own based on the code provided by the Example Kit. We need to do this in order to add a new keyboard listener and pass the event through its props.
- With our copied code in our clipboard, add a new code component to our file. Name it
MessageInput
, delete everything inside and paste in our copied code. Make sure you do a find/replace and change all theSearchBar
labels toMessageInput
and hit save. - If you wish you can uninstall the Example Kit as we no longer need it.
- Now we can create our keyboard event inside our
MessageInput
component. Inside ourinterface Props {}
add a new event prop.
// Pass the value as a string
onEnterKey: (value: string) => void
Now the fun part, lets add our onEnterKey()
event. It consists of two parts, the event itself and then attaching the event to the HTML <input/>
tag.
onEnterKey = (event) => {
const value = event.nativeEvent.target.value
// Enter Key Pressed
if (event.nativeEvent.keyCode === 13) {
this.props.onEnterKey ? this.props.onEnterKey(value) : null
}
}render() ...
Now to attach the event to our tag we will use onKeyPress
to handle the event every-time the user pounds a key into our input. That includes backspaces and selection-deletes. For a full list of React events reference this link.
<input
onChange={this.onChange}
onKeyPress={this.onEnterKey}
value={value}
placeholder={placeholder}
style={{ ...style, backgroundColor, color: textColor }}
/>
Let’s clean up the component by adding the CSS styles inside our constructor, remove the borderRadius
and absolute
properties, add outline: “none”
and set our width and height based on the components size.
...render() {
const { placeholder, backgroundColor, textColor } = this.props
const { value } = this.state const style: React.CSSProperties = {
width: this.props.width,
height: this.props.height,
border: "none",
outline: "none",
paddingLeft: 8,
paddingRight: 8,
}...
That’s all we need to do for the MessageInput
. Now save our component and we can move onto the chat window.
Pull it all together with the Chat Window
Create one last code component and name it ChatWindow
. Empty the file and create the base code with two import statements, the interface props, the class constructor and two css styles placed inside the constructor.
import * as React from "react";
import { PropertyControls, ControlType } from "framer";interface Props {
width: number;
height: number;
}export class ChatWindow extends React.Component<Props> {
render() {
const containerStyle: React.CSSProperties = {}
const listStyle: React.CSSProperties = {}
return (
<div style={containerStyle}></div>
);
}
}
Now we will import our ChatBubble
and MessageInput
components into our ChatWindow
. After doing so build out the html template using our imported components. Feel free to style your container and list however you like.
import * as React from "react";
import { PropertyControls, ControlType } from "framer";
import { ChatBubble } from "./ChatBubble";
import { MessageInput } from "./MessageInput";interface Props {
width: number;
height: number;
}export class ChatWindow extends React.Component<Props> { render() { const containerStyle: React.CSSProperties = {
width: this.props.width,
height: this.props.height,
background: '#FFFFFF',
borderRadius: "6px",
overflow: "hidden",
// Flex Styling
display: "flex",
flexDirection: "column",
} const listStyle: React.CSSProperties = {
height: "100%",
margin: 0,
padding: 8,
overflowY: "scroll",
listStyle: "none",
borderBottom: "1px #EEEEEE solid",
} return (
<div style={containerStyle}>
<ul style={listStyle}>
<ChatBubble width={"100%"} height={"auto"}/>
</ul>
<MessageInput
width={"100%"}
backgroundColor={"transparent"}
placeholder={"Ask a question..."}
/>
</div>
);
}
}
Let’s hit save and view our component in the Framer X canvas. After dragging the component on-screen you should see the same ChatBubble
component inside our list, and our MessageInput
component inside our container.
Since we gave our <ChatBubble />
a width
of 100%
and a height
of auto
, our ChatBubble
component can be duplicated inside the list and we will start to see a conversation feed arise.
Next we create a new method that will will hook into our MessageInput
and fire every time our onEnterKey
event is triggered. From there we will need to create a new state of data to hold messages in an array, those messages will then be mapped into our <ul>
and determine the properties for each ChatBubble
.
Let’s get code fancy
First, we setup our method then bind it to our component. From there we can create our state to hold the message array.
- Create a new method called
triggerMessage()
and pass along thevalue
string. - Right inside our class we’re going to open up the
constructor()
method and pass along the props. - Create a binding to our
triggerMessage
. - Build an empty array of data within our state.
- Fill out our
triggerMessage
method by using the React function,setState({})
. - Create an object of data that holds the message and which side to show the message.
- Add our value to our list of data.
export class ChatWindow extends React.Component<Props> {
constructor(props){
super(props);
this.triggerMessage = this.triggerMessage.bind(this);
this.state = {
data: []
}
} triggerMessage(value) {
this.setState({
data: [...this.state.data,
{
"side" : "left",
"message" : value
},
]
});
} render() ...
Next we attach the triggerMessage
method to our MessageInput
component inside our component template. We can do that easily by using the onEnterKey
prop we passed in our MessageInput
.
...<MessageInput
width={"100%"}
backgroundColor={"transparent"}
placeholder={"Ask a question..."}
onEnterKey={this.triggerMessage}
/>...
Now to create our message listing we will use a map function to sift through our data and add the messages based on our users input. Inside the map function we need to pass the index to calculate the list keys and then we can attach our value by using message={item.message}
and we attach our side conditional the same way.
This mapping will replace our HTML <ChatBubble />
tag, and instead the ChatBubble
will be placed inside the function.
...<ul style={listStyle}>
{this.state.data.map((item, index) => (
<ChatBubble
key={index}
width={'100%'}
height={'auto'}
message={item.message}
side={item.side}
/>
))}
</ul>...
If we save our component and head over to our Framer canvas, you’ll notice the first message has disappeared. This is because now our message list is being controlled by out state.data.
We can preview the ChatWindow
and test it out, if done correctly, you’ll see a new message appear in your list every time you type a message into the input and press the enter key.
Customize your chat bot
The next steps are based on however you want your chat bot to respond to the user input. For instance, maybe you want to listen for a key-word like “weather” and then display a message containing today’s weather. Or maybe we want our bot to respond to every message we send and it’s response would be randomly pulled from an data set of chatbot messages.
To keep it simple i’ll explain the steps on how to add a response method and then link to an example file in the store that you can explore for more ideas on how to control the bot responses.
- Make sure you’re inside the
ChatWindow
component file. - Create a new method under our
triggerMessage
calledsendReply
. - Inside the method add a
setTimeout()
function which will delay the response by a set time. We’ll use 1500 milliseconds in this example. - Then just as before, include the same
setState({})
function as we did for thetriggerMessage
. But instead of passing thevalue
as the message, we can include a custom string to push through. We also need to set the data side to “right”. - At the end of our
triggerMessage
method we will call our newsendReply()
method.
triggerMessage(value) {
this.setState({
data: [...this.state.data,
{
"side" : "left",
"message" : value
},
]
});
this.sendReply()
}sendReply() {
setTimeout(() => {
this.setState({
data: [...this.state.data,
{
"side" : "right",
"message" : "Offline till 9:00 AM"
},
]
});
}, 1500);
}
Now if we head back to the preview, you’ll notice after you type and hit send, a slight delay happens then the bot replies with our new string, Offline till 9:00 AM
and it will appear on the right side.
Working with code components in Framer X can be tricky if you’re a designer like me. But the best learning tool I’ve come across has been using the in-app store to download and learn from others. If you ran into trouble with this tutorial, you can download the final .framerx
file below.
If you want to check out a more advanced version of this chat bot you can download my free package in the Framer X Store. Search for “Chat Bot” in the store or click the link below.
Chat Bot Component — Framer X Store link.