Creating Chat Bubbles with curls in React Native (svg)

tobs_dl

Oluwatobi O

Posted on June 24, 2024

Creating Chat Bubbles with curls in React Native (svg)

Recently, while working on a project, I needed to create a chat feature with a UI similar to iMessage. When I reached the part where I had to implement the curls, I considered three options:

  • One: Use before and after pseudo element. (Challenge: pseudo classes and element do not work in react-native, hence option 2)
  • Two: Use two Views one for the curl and another to overlap it. Something like this

    <View style={styles.chatBubble}>
    <Text style={styles.text}> Hey whats up </Text>   
    <View style={styles.curl}></View>
    <View style={styles.curlOverlap}></View>
    </View>
    
  • Three: Use an Svg. Less work :) yay!

Initially, I decided to go with option 2 (using two view) but I didn't get the desired result, so I decided to go with using an Svg since I had a tight timeline.

Now enough with my back story in this article I will guide you on how to create a chat bubble curl using an svg in React native, while utilizing Tailwind instead of the stylesheet.

Before we start, here’s a preview of what we want to achieve:

Final result

Let's get started

First, we need to install nativewind to our expo app. Check here for installation guide Nativewind.

Next we need to install react-native-svg:

npm i react-native-svg
Enter fullscreen mode Exit fullscreen mode

Ok, now that we've got our development environment setup we can start coding.

To use our svg we can import SvgXml from react native svg. This converts a valid XML string into an SVG. Next, we define our XML string:

// ..... other imports .....
import { SvgXml } from 'react-native-svg';

// Xml strings for left and right curl svg
const curlRight = `<svg width="17" height="21" viewBox="0 0 17 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16.8869 20.1846C11.6869 20.9846 6.55352 18.1212 4.88685 16.2879C6.60472 12.1914 -4.00107 2.24186 2.99893 2.24148C4.61754 2.24148 6 -1.9986 11.8869 1.1846C11.9081 2.47144 11.8869 6.92582 11.8869 7.6842C11.8869 18.1842 17.8869 19.5813 16.8869 20.1846Z" fill="#2377F1"/>
</svg>
`

const curlLeft = `<svg width="17" height="21" viewBox="0 0 17 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0.11315 20.1846C5.31315 20.9846 10.4465 18.1212 12.1132 16.2879C10.3953 12.1914 21.0011 2.24186 14.0011 2.24148C12.3825 2.24148 11 -1.9986 5.11315 1.1846C5.09194 2.47144 5.11315 6.92582 5.11315 7.6842C5.11315 18.1842 -0.88685 19.5813 0.11315 20.1846Z" fill="white"/>
</svg>
`
Enter fullscreen mode Exit fullscreen mode

Now we can continue with creating our views.

// .... previous code ....
export default function HomeScreen() {
    const data  = [
        { content: 'Hello', time: "7:30pm", role: 'system', id: 1 },
        { content: "What's up", time: "7:31pm", role: 'user', id: 2 },
    ];

    return (
        <View className="flex flex-1">
            <ImageBackground source={require(`../../assets/images/bg.png`)} resizeMode="repeat" className="bg-slate-300 absolute -z-10 w-full h-full">
                <View className="flex-1 mt-3 mx-3">
                    <FlatList
                        initialNumToRender={3}
                        data={data}
                        showsVerticalScrollIndicator={false}
                        renderItem={({ item }) => {
                            if (item.role === 'user') {
                                return (
                                    <View key={item.id} className={`relative bg-[#2377F1] self-end p-3 px-6 rounded-3xl mt-2 ${item.id === data.length && 'mb-3'}`}>
                                        <View className="flex-row gap-3">
                                            <Text className="text-base max-w-[73%] text-white">{item.content}</Text>
                                            <Text className="text-sm text-white self-end">{item.time}</Text>
                                        </View>

                                    </View>
                                );
                            } else {
                                return (
                                    <View className={`relative bg-white p-3 pl-6 self-start rounded-3xl mt-2 ${item.id === data.length && 'mb-3'} `}>
                                        <View className="flex-row gap-3">
                                            <Text className="text-base max-w-[73%] flex-row">{item.content}</Text>
                                            <Text className="text-sm self-end">{item.time}</Text>
                                        </View>

                                    </View>
                                );
                            }
                        }}
                        keyExtractor={(item) => item.id.toString()}
                        className="mb-14"
                    />
                </View>
            </ImageBackground>
        </View>
    );
}

Enter fullscreen mode Exit fullscreen mode

At this stage, we still have a rounded view containing the message because we haven't added the SVG to our views. To add the SVG, we need to pass our XML string to the SvgXml tag, like this:

renderItem={({ item }) => {
    if (item.role === 'user') {
        return (
            <View key={item.id} className={`relative bg-[#2377F1] self-end p-3 px-6 rounded-3xl mt-2 ${item.id === data.length && 'mb-3'}`}>
                <View className="flex-row gap-3">
                    <Text className="text-base max-w-[73%] text-white">{item.content}</Text>
                    <Text className="text-sm text-white self-end">{item.time}</Text>
                </View>
                {/* Here for right curl */}
                <SvgXml xml={curlRight} width={20} height={20} className="absolute bottom-0 right-0 z-50" />
            </View>
        );
    } else {
        return (
            <View className={`relative bg-white p-3 pl-6 self-start rounded-3xl mt-2 ${item.id === data.length && 'mb-3'} `}>
                <View className="flex-row gap-3">
                    <Text className="text-base max-w-[73%] flex-row">{item.content}</Text>
                    <Text className="text-sm self-end">{item.time}</Text>
                </View>
                {/* Here for left curl */}
                <SvgXml xml={curlLeft} width={20} height={20} className="absolute bottom-0 left-[-.1rem] z-50" />
            </View>
        );
    }
}}

Enter fullscreen mode Exit fullscreen mode

By following these steps, you should now have chat bubbles with curls implemented in your React Native app using SVGs.

Conclusion

This approach not only simplifies the code but also enhances the visual appeal of your chat interface. Using SVGs provides greater flexibility and ensures that the curls look sharp on all screen sizes. You can check Chatbot-rn if you would like to build a chatbot with Gemini.

💖 💪 🙅 🚩
tobs_dl
Oluwatobi O

Posted on June 24, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related