Mobile Step 2: Chat UI
Build the chat interface — message bubbles, input field, and send button.
What You'll Learn
- React Native core components (View, Text, TextInput, TouchableOpacity)
- Styling with StyleSheet
- FlatList for scrollable message list
- Layout with flexbox
Step 1: Understanding the Component Structure
Our chat app will have this layout:
┌─────────────────────┐
│ Header │
├─────────────────────┤
│ │
│ Message List │
│ ┌─────────┐ │
│ │ User │ │
│ └─────────┘ │
│ ┌──────────┐ │
│ │ AI │ │
│ └──────────┘ │
│ │
├─────────────────────┤
│ Typing indicator │
├─────────────────────┤
│ [Input...] [Send] │
└─────────────────────┘
Step 2: Core Components
import { View, Text, TextInput, TouchableOpacity, FlatList, StyleSheet } from 'react-native';
<View> // Like <div> in web — a container
<Text> // Display text
<TextInput /> // Text input field
<TouchableOpacity> // Pressable button
<FlatList /> // Scrollable, optimized list
<StyleSheet> // Defines styles
Step 3: Define the Message Interface
interface Message {
id: number;
content: string;
sender: "user" | "ai";
timestamp?: string;
}
Step 4: State for Messages and Input
import { useState } from "react";
const [messages, setMessages] = useState<Message[]>([]);
const [input, setInput] = useState("");
messages— the array of chat messagesinput— the current text in the input field
Step 5: Render Messages with FlatList
<FlatList
data={messages}
keyExtractor={(item) => item.id.toString()}
renderItem={({ item }) => {
const isUser = item.sender === "user";
return (
<View style={[
styles.bubble,
isUser ? styles.userBubble : styles.aiBubble
]}>
<Text style={isUser ? styles.userText : styles.aiText}>
{item.content}
</Text>
</View>
);
}}
/>
data— the array to renderkeyExtractor— unique key for each itemrenderItem— function that returns the component for each item
Step 6: Input Row
<View style={styles.inputRow}>
<TextInput
style={styles.input}
value={input}
onChangeText={setInput}
placeholder="Type a message..."
/>
<TouchableOpacity style={styles.sendButton}>
<Text style={styles.sendButtonText}>Send</Text>
</TouchableOpacity>
</View>
Step 7: Styling with StyleSheet
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#F5F5F5",
},
bubble: {
maxWidth: "80%",
padding: 12,
borderRadius: 16,
marginBottom: 8,
},
userBubble: {
backgroundColor: "#007AFF",
alignSelf: "flex-end",
},
aiBubble: {
backgroundColor: "#E8E8E8",
alignSelf: "flex-start",
},
inputRow: {
flexDirection: "row",
padding: 12,
borderTopWidth: 1,
borderTopColor: "#DDD",
backgroundColor: "#FFF",
},
input: {
flex: 1,
borderWidth: 1,
borderColor: "#DDD",
borderRadius: 20,
paddingHorizontal: 16,
paddingVertical: 10,
fontSize: 16,
},
sendButton: {
backgroundColor: "#007AFF",
borderRadius: 20,
paddingHorizontal: 20,
paddingVertical: 10,
marginLeft: 8,
},
sendButtonText: {
color: "#FFF",
fontSize: 16,
fontWeight: "600",
},
});
Step 8: Build the Complete UI
Replace your App.tsx with the code from the final App.tsx (just the UI part — no API calls yet, use placeholder messages):
import React, { useState } from "react";
import { View, Text, TextInput, TouchableOpacity, FlatList, StyleSheet } from "react-native";
interface Message {
id: number;
content: string;
sender: "user" | "ai";
}
export default function App() {
const [messages, setMessages] = useState<Message[]>([]);
const [input, setInput] = useState("");
const handleSend = () => {
if (!input.trim()) return;
const newMsg: Message = { id: Date.now(), content: input, sender: "user" };
setMessages([...messages, newMsg]);
setInput("");
};
return (
<View style={styles.container}>
<FlatList
data={messages}
keyExtractor={(item) => item.id.toString()}
renderItem={({ item }) => (
<View style={[styles.bubble, item.sender === "user" ? styles.userBubble : styles.aiBubble]}>
<Text>{item.content}</Text>
</View>
)}
/>
<View style={styles.inputRow}>
<TextInput
style={styles.input}
value={input}
onChangeText={setInput}
placeholder="Type a message..."
/>
<TouchableOpacity style={styles.sendButton} onPress={handleSend}>
<Text style={styles.sendButtonText}>Send</Text>
</TouchableOpacity>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: "#F5F5F5", paddingTop: 50 },
bubble: { maxWidth: "80%", padding: 12, borderRadius: 16, marginBottom: 8 },
userBubble: { backgroundColor: "#007AFF", alignSelf: "flex-end" },
aiBubble: { backgroundColor: "#E8E8E8", alignSelf: "flex-start" },
inputRow: { flexDirection: "row", padding: 12, borderTopWidth: 1, borderTopColor: "#DDD" },
input: { flex: 1, borderWidth: 1, borderColor: "#DDD", borderRadius: 20, paddingHorizontal: 16, paddingVertical: 10 },
sendButton: { backgroundColor: "#007AFF", borderRadius: 20, paddingHorizontal: 20, paddingVertical: 10, marginLeft: 8 },
sendButtonText: { color: "#FFF", fontSize: 16, fontWeight: "600" },
});
Verification
- Type a message in the input field
- Tap Send
- The message appears as a blue bubble on the right
- No AI reply yet (that's step 3)