React Native - More on Custom Components

This lesson is part of a series on React Native.

Step 5 - Create a CustomText component

In the components folder, create CustomText:

import { Text } from 'react-native'
import { Colors, Fonts, Spacing } from '../Theme'

const CustomText = ({ style, title = false, ...props }) => {
  
  let color = Colors.text
  let fontSize = Fonts.medium
  if(title){
    fontSize = Fonts.large
    color = Colors.primary
  }

  return (
    <Text 
      style={[{color, fontSize, fontWeight:"bold"}, style]}
      {...props}
    />
  )
}

export default CustomText

Notes:

Updated App.js:

import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View, TextInput, Button, Image} from 'react-native';
import Logo from './assets/icon.png';
import CustomScreen from './components/CustomScreen';
import CustomText from './components/CustomText';    ///////////////// IMPORT THIS

export default function App() {
  return (
    <CustomScreen>
      <Image source={Logo} style={{ width: 100, height: 100, marginBottom: 20 }} />
      
      {/* <Text style={styles.title}>Your App Name</Text> */} // REPLACE WITH (note title is true):
      <CustomText title>Your App Name</CustomText>

      // ADD THIS JUST TO SHOW YOU DON'T ALWAYS NEED TO USE THE CustomText
      <Text>Welcome to your app!</Text> 

      {/* <Text style={styles.label}>Email</Text> */} // REPLACE WITH THIS:
      <CustomText style={styles.label}>Email</CustomText>
      <TextInput placeholder="Enter your email" style={styles.input} />

      {/* <Text style={styles.label}>Password</Text> */} // REPLACE WITH THIS
      <CustomText style={styles.label}>Password</CustomText>
      <TextInput placeholder="Enter your password" style={styles.input} secureTextEntry />
      <View style={styles.btnContainer}>
        <Button title="Submit" style={styles.btn} onPress={() => {console.log('Button pressed!')}} />
      </View>
      <StatusBar style="auto" />
    </CustomScreen>
  );
}

const styles = StyleSheet.create({
  // title:{                   //// REMOVE
  //   fontSize: 24,
  //   marginBottom: 20,
  // },
  input: {
    height: 40,
    borderColor: 'gray',
    borderWidth: 1,
    marginBottom: 10,
    width: '80%',
    paddingHorizontal: 10,
  },
  label:{
    marginBottom: 5,
    fontWeight: 'bold',
    alignItems: 'flex-start',
    width: '80%',
  },
  btnContainer: {
    width: '80%',
    marginTop: 10,
  }

});

Create a Spacer componnent

Now we'll make a simple little component that will make our lives easier....

Create Spacer.jsx in the components folder:

import { View } from 'react-native'

const Spacer = ({ width = "100%", height = 40 }) => {
  return (
    <View style={{ width, height }} />
  )
}

export default Spacer

Import the component in App.js:

import Spacer from './components/Spacer'

Then add it under the Text element that says 'Welcome to your app!' in App.js:

<Spacer height={40} />

Create a CustomButton component

Remember that you can't really apply styles to the Button component that comes with React Native. So a custom button is usually the first component thing you create.

Create CustomButton.jsx in the components folder:

import { Pressable, StyleSheet } from 'react-native'
import { Colors} from '../Theme'

function CustomButton({ style, ...props }) {

  return (
    <Pressable style={[styles.btn, style]} {...props} />
  )
}
const styles = StyleSheet.create({
  btn: {
    backgroundColor: Colors.primary,
    padding: 18,
  }
})

export default CustomButton

Then update App.js:

import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View, TextInput, Button, Image} from 'react-native';
import Logo from './assets/icon.png';
import CustomScreen from './components/CustomScreen';
import CustomText from './components/CustomText';
import Spacer from './components/Spacer';
import CustomButton from './components/CustomButton';   ///////////////////Import it

export default function App() {
  return (
    <CustomScreen>
      <Image source={Logo} style={{ width: 100, height: 100, marginBottom: 20 }} />
      <CustomText title>Your App Name</CustomText>
      <Text>Welcome to your app!</Text>
      <Spacer height={40} />
      <CustomText style={styles.label}>Email</CustomText>
      <TextInput placeholder="Enter your email" style={styles.input} />
      <CustomText style={styles.label}>Password</CustomText>
      <TextInput placeholder="Enter your password" style={styles.input} secureTextEntry />
      
      // REMOVE THIS
      {/* <View style={styles.btnContainer}>
        <Button title="Submit" style={styles.btn} onPress={() => {console.log('Button pressed!')}} />
      </View> */}

      // ADD THIS
      <CustomButton 
        style={{margin: 20, width: '80%'}}
        onPress={() => console.log('Custom Button Pressed!')}>
        <Text style={{color: 'white', textAlign: 'center', fontWeight: 'bold'}}>Custom Button</Text>
      </CustomButton>

      // ADD THIS JUST TO DEMO SIDE BY SIDE BUTTONS
      <View style={{flexDirection:'row', columnGap: 10}}>
        <CustomButton onPress={() => console.log('Custom Button Pressed!')}>
          <Text style={{color: 'white', textAlign: 'center', fontWeight: 'bold'}}>Custom Button</Text>
        </CustomButton>
        <CustomButton onPress={() => console.log('Custom Button Pressed!')}>
          <Text style={{color: 'white', textAlign: 'center', fontWeight: 'bold'}}>Custom Button</Text>
        </CustomButton>
      </View>
      
      <StatusBar style="auto" />
    </CustomScreen>
  );
}

const styles = StyleSheet.create({
  input: {
    height: 40,
    borderColor: 'gray',
    borderWidth: 1,
    marginBottom: 10,
    width: '80%',
    paddingHorizontal: 10,
  },
  label:{
    marginBottom: 5,
    fontWeight: 'bold',
    alignItems: 'flex-start',
    width: '80%',
  },
  // btnContainer: { //////////// REMOVE
  //   width: '80%',
  //   marginTop: 10,
  // }

});

You can style a custom button when it's being pressed. Update CustomButton.jsx:

import { Pressable, StyleSheet } from 'react-native'
import { Colors} from '../Theme'

function CustomButton({ style, ...props }) {

  return (
    // PASS IN A FUNCTION FOR THE STYLE PROP, IF THE PRESSED PARAM IS TRUE THEN IT'S BEING PRESSED
    <Pressable 
      style={({ pressed }) => [styles.btn, pressed && styles.pressed, style]} 
      {...props}
    />
  )
}
const styles = StyleSheet.create({
  btn: {
    backgroundColor: Colors.primary,
    padding: 18,
  },
  /////////////////////// Add styling for the pressed state
  pressed: {
    opacity: 0.5
  },
})

export default CustomButton