Your Own Components
A component is a function that returns JSX: how to name it, export it, and build a screen out of small pieces.
function Greeting() { return <Text>Hi</Text>; } // used as <Greeting />A component is a function that returns JSX. That is the whole definition. There is no class to extend, no registration step and no template file. You use it by writing its name as a tag: <PriceTag /> calls the function.
Naming and exporting
The name must start with a capital letter. JSX treats a lowercase tag as a built-in element name, and React Native has no element called priceTag, so the render fails. The two ways of writing the function are equivalent: a function declaration, or a const holding an arrow function. Exports follow normal module rules. export default function PriceTag() is shorthand for declaring PriceTag and then writing export default PriceTag; underneath. Default export plus one component per file is the usual convention, but a file may use named exports for a group of small related components.
Composing
Components nest. A screen is normally one component that arranges a handful of smaller ones. Keep one concern per component: a row renders a row, a screen places rows. When you have to scroll to read a component's JSX, split it.
What trips people up
A component returns one element. Two siblings at the top level will not parse, so wrap them in a <View>, or in a fragment, covered later in this stage. Returning nothing renders nothing, so return null is legal and useful; a missing return is a bug.
Example
// App.tsx
import { StyleSheet, Text, View } from "react-native";
// A component is a plain function returning JSX. Capital first letter, always:
// a lowercase tag is read as a built-in element name, not as your function.
function PriceTag() {
return (
<View style={styles.tag}>
<Text style={styles.amount}>12.00</Text>
<Text style={styles.note}>includes tax</Text>
</View>
);
}
// The same thing as an arrow function held in a const. Longhand above,
// shorthand here; React cannot tell the difference. Pick one per project.
const SoldOut = () => <Text style={styles.sold}>Sold out</Text>;
export default function App() {
return (
<View style={styles.screen}>
<PriceTag />
<SoldOut />
{/* A component can be used as many times as you like. */}
<PriceTag />
</View>
);
}
const styles = StyleSheet.create({
screen: { flex: 1, paddingTop: 80, paddingHorizontal: 24, gap: 16 },
tag: { borderWidth: 1, borderColor: "#CCCCCC", borderRadius: 8, padding: 12 },
amount: { fontSize: 20, fontWeight: "700" },
note: { fontSize: 12, color: "#666666" },
sold: { fontSize: 14, color: "#B3261E" },
});
// On screen: a bordered box with "12.00" and "includes tax", the red line
// "Sold out" under it, then the same bordered box again.When to use it
- Pull a repeated row out of a long screen file into a component of its own
- Set up a components folder where each file exports one component
- Reuse the same badge in three screens without copying its styles
More examples
One component per file
A blue circle with the initials AD, centred near the top of the screen. The point is the file layout: the component and the styles it owns live together, and the screen imports it by path. A default export has no name of its own at the import site, so the importing file chooses one, which is why the file and the component should share a name.
// components/Avatar.tsx
import { StyleSheet, Text, View } from "react-native";
export default function Avatar() {
return (
<View style={styles.circle}>
<Text style={styles.initials}>AD</Text>
</View>
);
}
const styles = StyleSheet.create({
circle: {
width: 48,
height: 48,
borderRadius: 24, // half the width makes a circle; "50%" also works here
alignItems: "center",
justifyContent: "center",
backgroundColor: "#0A7EA4",
},
initials: { color: "#FFFFFF", fontWeight: "600" },
});
// App.tsx
import { StyleSheet, View } from "react-native";
import Avatar from "./components/Avatar"; // default import: name it what you like
export default function App() {
return (
<View style={appStyles.screen}>
<Avatar />
</View>
);
}
const appStyles = StyleSheet.create({
screen: { flex: 1, paddingTop: 80, alignItems: "center" },
});Splitting an overgrown screen
The heading "Today", the grey line "3 of 5 done", then a hairline rule. All four components are in one file, which is fine while the pieces are small and only this screen uses them. Do this when a screen's JSX stops fitting on one page; move the pieces to their own files when a second screen needs them.
import { StyleSheet, Text, View } from "react-native";
// Each piece names one concern. The screen only arranges them.
function Title() {
return <Text style={styles.title}>Today</Text>;
}
function Summary() {
return <Text style={styles.summary}>3 of 5 done</Text>;
}
function Divider() {
// A view with a height and no children is how you draw a line.
return <View style={styles.divider} />;
}
export default function HomeScreen() {
return (
<View style={styles.screen}>
<Title />
<Summary />
<Divider />
</View>
);
}
const styles = StyleSheet.create({
screen: { flex: 1, paddingTop: 80, paddingHorizontal: 24 },
title: { fontSize: 28, fontWeight: "700" },
summary: { fontSize: 14, color: "#666666", marginTop: 4 },
divider: { height: 1, backgroundColor: "#E0E0E0", marginTop: 16 },
});Named exports for a group of small components
Two short labels, NEW in blue and SALE in red, stacked with eight points between them. Reach for named exports when several components are too small to deserve files of their own and always travel together. Mixing one default export with named exports in the same file is legal but tends to make imports inconsistent across a project.
// components/badges.tsx
import { StyleSheet, Text } from "react-native";
// A file can export several components by name instead of one by default.
export function NewBadge() {
return <Text style={styles.badge}>NEW</Text>;
}
export function SaleBadge() {
return <Text style={[styles.badge, styles.sale]}>SALE</Text>;
}
const styles = StyleSheet.create({
badge: { fontSize: 11, fontWeight: "700", color: "#0A7EA4" },
sale: { color: "#B3261E" },
});
// App.tsx
import { StyleSheet, View } from "react-native";
// Named imports must match the exported names exactly, and take braces.
import { NewBadge, SaleBadge } from "./components/badges";
export default function App() {
return (
<View style={styles.screen}>
<NewBadge />
<SaleBadge />
</View>
);
}
const styles = StyleSheet.create({
// gap spaces the children; it has been supported since 0.71.
screen: { flex: 1, paddingTop: 80, alignItems: "center", gap: 8 },
});
Discussion