Blog home

components images react native videos how to

Seamlessly Integrating Images and Videos into Your React Native Projects

Explore the effortless integration of images and videos into React Native projects with TwicPics Components, leveraging seamless lazy loading for optimal performance.

Seamlessly Integrating Images and Videos into Your React Native Projects

Introduction

Integrating images and short videos into React Native projects can be a breeze!

In this step-by-step tutorial, we'll delve into the seamless process of incorporating compelling visual content using TwicPics Components.

We'll first showcase the simplicity of displaying an image while applying best practices: CLS optimization, LQIP, pixel-perfect, smart cropping, and ideal compression.

Once we've mastered this, we'll seamlessly extend our demonstration to highlight that displaying a short video is just as straightforward.

Incidentally, we'll see that, with TwicPics Components, lazy loading is out of the box. It is usable for both images and videos in any container, not just the constraining FlatList component.

Create your React Native project

Environnement

In this tutorial, we'll create a React Native application using Expo Go and run it on an iPhone 15 emulator running on macOS.

You can, of course, achieve the same results using the React Native CLI, choosing an Android emulator, and working on another operating system.

Setup Wizard

Creating a React Native application using Expo Go Quickstart is as simple as:

# creates a new project using Expo Go Quickstart
yarn create expo-app AwesomeProject

Just follow the instructions and accept the installation requests. Once the project has been successfully created, you can cd into its directory.

Start your application

To start the development server with Expo, use the following command:

# starts the development server
yarn start

If everything proceeds as expected, a QR code will be generated in the terminal. This QR code enables you to test your React Native application directly on your mobile device.

In this tutorial, we'll use our iPhone 15 emulator, which we launch by pressing the i key.

And this is what we should get:

React Native is running.
React Native is running.

If something isn't working correctly here, we invite you to check the Expo Go documentation.

Install and configure TwicPics Components

Now that our React Native application is up and running, it is time to install and configure the React Native version of the TwicPics components.

In the following, we will use our test domain:https://demo.twic.pics
If you don't have a TwicPics domain yet, you can easily create your own for free.

Install TwicPics Components

After stopping the development server, type the following to add the @twicpics/components package to your project. As we plan to use the TwicVideo component, we also need to install expo-av:

# using yarn
yarn add @twicpics/components

# only necessary if you intend to use `TwicVideo` component
yarn add expo-av@13.5.0
To install expo-av in a React Native CLI app you must follow these additional installation instructions.

Configure TwicPics Components

The configuration of the TwicPics components must be performed at the main entry point of the application.

In our case, modify the file app.js as follows:

// app.js
/* imports TwicPics Components */
import { installTwicPics } from '@twicpics/components/react-native';

installTwicPics({
  "domain": `https://demo.twic.pics/`, // set your very domain here
  "debug": true, // this will output useful logs to the terminal
  "maxDPR": 3, // set your desired maximum DPR here; default is 2
});

export default function App() {
/* file rest unchanged */

The setup is now complete, and that was the most intricate phase. We're now ready to showcase our images and videos.

Displaying Images with TwicPics Components

To display an image with TwicPics Components we just have to add TwicImg component to the layout and set its src attribute.

Here we display sea-anemone.jpg (jpeg - 2400 x 1800 px - 761 kB).

Let's reopen app.js and modify it as follows:

// app.js
import { StyleSheet, Text, View } from 'react-native';
import {
    installTwicPics,
    TwicImg, // import TwicImg
} from '@twicpics/components/react-native';


installTwicPics({
  "domain": `https://demo.twic.pics/`,
  "debug": true,
  "maxDPR": 3,
});

export default function App() {
  return (
    <View style={styles.container}>
      <TwicImg
        src="sea-anemone.jpg" // image we want to display
      />
      {/* testing CLS optimization */}
      <Text>There is no CLS here.</Text>
    </View>
  );
}
/* file rest unchanged */

And this is what we should get now:

Just to easy...

What happened here?

  • As we haven't set a value for the ratio property, we get a square image.
  • The display area has been reserved and there is no CLS.
  • A Low-Quality Image Placeholder (LQIP) enhances the user experience.

An important point to note is that we haven't specified a display size for the image. Here, it spans the entire width of the viewport. Let's clarify this point with the following example.

The significance of the display context

Since TwicPics components are designed following the layout-driven pattern, it involves managing the size of their container.

Let's modify our app.js file to display two variants of our master file. One has a size of 300px, and the other has a size of 200px. We achieve this by specifying a width for the View components encapsulating our TwicImg elements:

// app.js
import { StyleSheet, Text, View } from 'react-native';
import { installTwicPics, TwicImg, } from '@twicpics/components/react-native';


installTwicPics({
  "domain": `https://demo.twic.pics/`,
  "debug": true,
  "maxDPR": 4, // high value to test dpr awareness
});

export default function App() {
  return (
    <View style={styles.container}>
      <View 
        style={{width: 300}} // constrains width to 300px
      >
        <TwicImg src="sea-anemone.jpg"/>
      </View>
      <Text style={styles.text}>There is no CLS here.</Text>
      <View
        style={{width: 200}} // constrains width to 200px
      >
        <TwicImg src="sea-anemone.jpg"/>
      </View>
      <Text>There is no CLS here either.</Text>
    </View>
  );
}
You may have noticed that we've increased the max DPR value up to '4'. Please note that we strongly advise against using this exaggerated value. It is solely used here for illustrative purposes.

Here is the result of our modifications:

We have to be aware.
We have to be aware.

As shown in the console, using the same master file, we effectively get two different-sized images:

These results might seem surprising compared to the sizes we had specified (300x300 and 200x200, respectively). However, considering that the iPhone 15 has a DPR of 3, these values make sense instead.

They showcase that TwicPics API has not only accurately provided variants matching the dimensions of the actual container but has also taken into account the actual pixel density. For short, TwicPics components are context-aware.

Lazy Loading without FlatList?

Implementing lazy loading in React Native can be challenging, even with dedicated libraries. A direct and simplified solution would be to use the FlatList component. However, there again, it necessitates writing more cumbersome code.

With TwicPics Components, lazy loading requires no extra effort. It comes built-in, eliminating the need for manual implementation.

Let's display our images in both a vertical and horizontal scroll.

// app.js
import { ScrollView, StyleSheet, Text, View } from 'react-native';
import {
  installTwicPics,
  TwicImg
} from '@twicpics/components/react-native';


installTwicPics({
  "domain": `https://demo.twic.pics/`,
  "debug": true,
  "maxDPR": 3,
});

// images to be displayed
const images = [
  `clown-fish.jpg`,
  `coral-reef.jpg`,
  `sea-anemone.jpg`,
  `sea-turtle.jpg`
];

export default function App() {
  return (
    <ScrollView>
      <View style={styles.container}>
        { images.map( ( image, i ) => (
          <View key={ i }>
            <TwicImg
              src={ image }
              style={styles.img}
              ratio="3/4" // displays an image with aspect ratio = 3/4
            />
          </View>
          ) )
        }
        <Text>➡➡ An horizontal slider with lazy loaded images ➡➡</Text>
        <ScrollView
          horizontal 
          style={ styles.slider }
        >
          { images.map( ( image, i ) => (
              <View style={ styles.sliderItem } key={ i }>
                <TwicImg
                  src={ image }
                  style={styles.img}
                  ratio="16/9" // displays an image with aspect ratio = 16/9
                />
              </View>
            ) ) 
          }
        </ScrollView>
        
      </View>
    </ScrollView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
  img: {
    padding: 20,
  },
  slider: {
    "marginHorizontal": 20,
  },
  sliderItem: {
    width: 500,
  },
});

You will note that in this example, we have specified a portrait aspect ratio for the vertical scroll and 16:9 for the horizontal one.

This is what we should get now:

Lazy Loading is a breeze.

As highlighted in the console, images are loaded near or within the viewport, and lazy loading seamlessly operates for both vertical and horizontal scrolling situations.

As with previous examples, CLS is optimized, and LQIP enhances the user experience.

Intermission

We have seen how easy it is to efficiently and effortlessly display images in a React Native project.

The next step of our journey will show that efficiently displaying short videos is no more complicated.

Displaying Short Videos with TwicPics Components

Let's revisit our initial example, but this time by using TwicVideo component and jellyfish.mp4 (mp4 - 1920 x 1080 px - 10.30 Mb) as the video we want to showcase.

Our app.js file becomes:

// app.js
import { StyleSheet, Text, View } from 'react-native';
import {
    installTwicPics,
    TwicVideo, // import TwicVideo
} from '@twicpics/components/react-native';


installTwicPics({
  "domain": `https://demo.twic.pics/`,
  "debug": true,
  "maxDPR": 3,
});

export default function App() {
  return (
    <View style={styles.container}>
      <TwicVideo
        src="video/sea-anemone.mp4" // video we want to display
      />
      {/* testing CLS optimization */}
      <Text>There is no CLS here.</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});

Here is the result:

I am jellyfish-struck.

Similarly to the image scenario:

  • We get a square video as we haven't set a value for the ratio property.
  • The display area has been reserved, and there is no CLS.
  • A Low-Quality Image Placeholder (LQIP) enhances the user experience.

The Return of the Display Context

The layout-driven pattern also applies to videos, as demonstrated in the modified app.js file below:

// app.js
import { StyleSheet, Text, View } from 'react-native';
import { installTwicPics, TwicVideo, } from '@twicpics/components/react-native';


installTwicPics({
  "domain": `https://demo.twic.pics/`,
  "debug": true,
  "maxDPR": 4, // high value to test dpr awareness
});

export default function App() {
  return (
    <View style={styles.container}>
      <View 
          style={{width: 300}} // constrains width to 300px
        >
          <TwicVideo src="video/jellyfish.mp4"/>
      </View>
      <Text style={styles.text}>There is still no CLS here.</Text>
      <View
        style={{width: 200}} // constrains width to 200px
      >
        <TwicVideo src="video/jellyfish.mp4"/>
      </View>
      <Text>Nor here either.</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});
We are still aware.

Again, using the same master file, we get two different variants, each fitting its container's size while considering the actual pixel density:

Lazy Loaded Videos?

Similarly to TwicImg, lazy loading is seamlessly integrated into TwicVideo, eliminating the need for manual and tedious implementation.

Let's demonstrate this by displaying a list of videos in portrait format (ratio="3/4") within a vertical ScrollView and the same video list within a widescreen aspect ratio (ratio="16/9) within a horizontal one.

import { ScrollView, StyleSheet, Text, View } from 'react-native';
import { installTwicPics, TwicVideo } from '@twicpics/components/react-native';


installTwicPics({
  "domain": `https://demo.twic.pics/`,
  "debug": true,
  "maxDPR": 3,
});

// videos to be displayed
const videos = [
  `turtle-swimming.mp4`,
  `coral-reef.mp4`,
  `sea-anemone.mp4`,
  `jellyfish.mp4`
];

export default function App() {
  return (
    <ScrollView>
      <View style={styles.container}>
        { videos.map( ( url, i ) => (
          <View key={ i }>
            <TwicVideo
              src={ `video/${ url } ` }
              style={ styles.item }
              ratio="3/4" // displays video with aspect ratio = 3/4
            />
          </View>
          ) )
        }
        <Text>➡➡ An horizontal slider with lazy loaded videos ➡➡</Text>
        <ScrollView
          horizontal 
          style={ styles.slider }
        >
          { videos.map( ( url, i ) => (
              <View style={ styles.sliderItem } key={ i }>
                <TwicVideo
                  src={ `video/${ url } ` }
                  style={ styles.item }
                  ratio="16/9" // displays video with aspect ratio = 16/9
                />
              </View>
            ) ) 
          }
        </ScrollView>
      </View>
    </ScrollView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
  item: {
    padding: 20,
  },
  slider: {
    "marginHorizontal": 20,
  },
  sliderItem: {
    width: 500,
  },
});

This is the result on our device:

Lazy Loading is definitively a breeze.

If we check the console, similarly to the case of images, our videos are effectively requested when they are in or approaching the viewport.

Appendix

For your reference, here is the list of media master files used in this tutorial.

AssetFormatDimensionsSize
clown-fish.jpgJPEG2400 x 1548 px730 kB
coral-reef.jpgJPEG5963 x 3353 px2.10 MB
sea-anemone.jpgJPEG2400 x 1800 px761 kB
sea-turtle.jpgJPEG2395 x 2994 px964 kB
turtle-swimming.mp4MP41920 x 1080 px31.85MB
coral-reef.mp4MP41280 x 720 px3.66MB
sea-anemone.mp4MP41920 x 1080 px8.16MB
jellyfish.mp4MP41920 x 1080 px10.30MB

Conclusion

Efficiently and effortlessly integrating images and short videos into a React Native project is a breeze with TwicPics Components.

This tutorial briefly covered some essential aspects of these components, such as context awareness and integrated lazy loading.

With context awareness, automatic adaptation to various DPR, orientations, and viewport sizes is achieved from a single master file. This eliminates the need for complex technical adjustments, allowing developers to concentrate solely on templating.

Integrated lazy loading also eliminates technical difficulties. Combined with implementing LQIP (Low-Quality Image Placeholder), it ensures that media content loads seamlessly only when needed, optimizing overall performance and user experience.

If you want to explore more possibilities offered by TwicPics Components for React Native, please feel free to run the dedicated example project from its repository.

TwicPics Components are free, open-source, and available in various frameworks. The only requirement to use them is to have a TwicPics account. If you don't already have one, you can easily create your own TwicPics account for free.

Feel free to open an issue if you encounter any problems or have ideas for new features.