Empty State

A simple screen to display a message, image or button when there's no content.

Last updated on

Edit on GitHub

Manual

Copy and paste the following code into your project. component/base/empty-state

import React, { createContext, ReactNode, useMemo } from "react";import {  StyleSheet,  Text,  TextStyle,  TouchableOpacity,  View,  ViewStyle,} from "react-native";import type {  IButton,  IEmptyContent,  IEmptyContextValue,  IEmptyDescription,  IEmptyHeader,  IEmptyMedia,  IEmptyProps,  IEmptyTitle,} from "./types";const EmptyContext = createContext<IEmptyContextValue | undefined>(undefined);export const Empty: React.FC<IEmptyProps> = ({  children,  variant = "default",  style,}) => {  const contextValue = useMemo<IEmptyContextValue>(    () => ({      variant,    }),    [variant],  );  return (    <EmptyContext.Provider value={contextValue}>      <View        style={[          styles.empty,          variant === "outline" && styles.emptyOutline,          style,        ]}      >        {children}      </View>    </EmptyContext.Provider>  );};export const EmptyHeader: React.FC<IEmptyHeader> = ({ children, style }) => {  return <View style={[styles.emptyHeader, style]}>{children}</View>;};export const EmptyMedia: React.FC<IEmptyMedia> = ({  children,  variant = "icon",  style,}) => {  return (    <View      style={[        styles.emptyMedia,        variant === "icon" && styles.emptyMediaIcon,        style,      ]}    >      {children}    </View>  );};export const EmptyTitle: React.FC<IEmptyTitle> = ({ children, style }) => {  return <Text style={[styles.emptyTitle, style]}>{children}</Text>;};export const EmptyDescription: React.FC<IEmptyDescription> = ({  children,  style,}) => {  return <Text style={[styles.emptyDescription, style]}>{children}</Text>;};export const EmptyContent: React.FC<IEmptyContent> = ({ children, style }) => {  return <View style={[styles.emptyContent, style]}>{children}</View>;};export const EmptyButton: React.FC<IButton> = ({  children,  variant = "default",  size = "md",  onPress,  style,}) => {  return (    <TouchableOpacity      style={[        styles.button,        variant === "outline" && styles.buttonOutline,        size === "sm" && styles.buttonSm,        size === "md" && styles.buttonMd,        size === "lg" && styles.buttonLg,        style,      ]}      onPress={onPress}      activeOpacity={0.7}    >      {typeof children === "string" ? (        <Text          style={[            styles.buttonText,            variant === "outline" && styles.buttonTextOutline,            size === "sm" && styles.buttonTextSm,          ]}        >          {children}        </Text>      ) : (        children      )}    </TouchableOpacity>  );};const styles = StyleSheet.create({  empty: {    backgroundColor: "transparent",    borderRadius: 16,    padding: 48,    alignItems: "center",    justifyContent: "center",    minHeight: 400,  } as ViewStyle,  emptyOutline: {    borderWidth: 2,    borderColor: "#333333",    borderStyle: "dashed",  } as ViewStyle,  emptyHeader: {    alignItems: "center",    justifyContent: "center",    width: "100%",  } as ViewStyle,  emptyMedia: {    marginBottom: 32,    alignItems: "center",    justifyContent: "center",  } as ViewStyle,  emptyMediaIcon: {    width: 96,    height: 96,    borderRadius: 24,    backgroundColor: "#1a1a1a",    alignItems: "center",    justifyContent: "center",  } as ViewStyle,  emptyTitle: {    fontSize: 28,    fontWeight: "600",    color: "#ffffff",    marginBottom: 16,    textAlign: "center",  } as TextStyle,  emptyDescription: {    fontSize: 16,    color: "#999999",    textAlign: "center",    lineHeight: 24,    paddingHorizontal: 20,  } as TextStyle,  emptyContent: {    marginTop: 32,    alignItems: "center",    justifyContent: "center",  } as ViewStyle,  button: {    paddingHorizontal: 24,    paddingVertical: 12,    borderRadius: 8,    alignItems: "center",    justifyContent: "center",    backgroundColor: "#ffffff",  } as ViewStyle,  buttonOutline: {    backgroundColor: "transparent",    borderWidth: 1,    borderColor: "#333333",  } as ViewStyle,  buttonSm: {    paddingHorizontal: 16,    paddingVertical: 8,  } as ViewStyle,  buttonMd: {    paddingHorizontal: 24,    paddingVertical: 12,  } as ViewStyle,  buttonLg: {    paddingHorizontal: 32,    paddingVertical: 16,  } as ViewStyle,  buttonText: {    fontSize: 14,    fontWeight: "500",    color: "#000000",  } as TextStyle,  buttonTextOutline: {    color: "#ffffff",  } as TextStyle,  buttonTextSm: {    fontSize: 12,  } as TextStyle,});

Usage

import {  Empty,  EmptyButton,  EmptyContent,  EmptyDescription,  EmptyHeader,  EmptyMedia,  EmptyTitle,} from "@/components/base/empty-state";import React from "react";import { StyleSheet, Text } from "react-native";import { SafeAreaView } from "react-native-safe-area-context";import { Feather } from "@expo/vector-icons";import { SymbolView } from "expo-symbols";export default function App() {  return (    <SafeAreaView style={styles.container}>      <Empty>        <EmptyHeader>          <EmptyMedia            variant="icon"            style={{              backgroundColor: "#0a0a0a",            }}          >            <SymbolView name="cube.box.fill" size={60} tintColor={"#fff"} />          </EmptyMedia>          <EmptyTitle>No Messages</EmptyTitle>          <EmptyDescription>            You don't have any messages yet. Start a conversation to get            started.          </EmptyDescription>        </EmptyHeader>        <EmptyContent>          <EmptyButton            variant="default"            onPress={() => {              console.log("New Message");            }}          >            New Message          </EmptyButton>        </EmptyContent>      </Empty>    </SafeAreaView>  );}const styles = StyleSheet.create({  container: {    flex: 1,    justifyContent: "center",    alignItems: "center",    backgroundColor: "#000000",  },});

Props

IButton

React Native