Skip to content

Build A Miniflix in 10 Minutes

Developers are constantly faced with challenges of building complex products every single day. And there are constraints on the time needed to build out the features of these products.

Engineering and Product managers want to beat deadlines for projects daily. CEOs want to roll out new products as fast as possible. Entrepreneurs need their MVPs like yesterday. With this in mind, what should developers do?

In this tutorial, we’ll quickly build out a Mini Netflix in 10 minutes. In fact, I think we might build it less time.

An excited entrepreneur just approached you to build a video service. A service where users can quickly upload short videos and share on twitter for their friends to view. Let’s list out the features of this app.

  • Users should be able to sign up and log in.
  • Registered/Logged-in users should be able to upload short videos of about 20 – 30 seconds.
  • Registered/Non-registered users should be able to view all uploaded videos on the platform on a dashboard.
  • Users should be able to share any of the videos on twitter.

Now, here’s the catch! T’challa of Wakanda wants to invest in some startups today, so the entrepreneur needs to demo the MVP of the video service in 10 minutes from now.

I know you are screaming your heart right now. It’s totally okay to cry and let the world know about your problems and challenges, but after much ado shedding tears, will the app be ready in 8 minutes? Well, sorry – tears can’t build an app!

It’s possible to build the MVP our entrepreneur is asking for. Let me show you how! Ready your editor, your command line and anxious fingers. Let’s get to work!!!

We’ll use React to build out the app. Facebook has a tool, create-react-app that can scaffold a progressive web app out of the box in less than a minute. If you don’t have it installed, please install and run the command below in your terminal:

create-react-app miniflix
cd miniflix

Go ahead and open up public/index.html. Pull in bootstrap and add it just after the link to the favicon.

<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">Code language: HTML, XML (xml)

Go ahead and install the following packages from your terminal:

npm install auth0-js react-router@3.0.0 jwt-decode axios
Code language: CSS (css)
  • auth0-js – For authentication
  • react-router – For routing within our app
  • jwt-decode – For decoding the JSON Web Token in our app
  • axios – For making network requests.

Open up your src directory and create a components and utils folder. In the utils folder, create a file, AuthService.js and add the code here to it. I explained how to handle the authentication in this tutorial, so check it out to ensure you are on the right track.

We’ll create 4 components in the components folder. Callback.js, Display.js, Nav.js and Upload.js

The Callback component basically stores our authentication credentials and redirects back to the upload route in our app.

The Display component will be dashboard for viewing all videos.

The Nav component will be the navigation that all pages in the app will share.

The Upload component will handle uploading of videos by registered users.

Add this piece of code below to components/Callback.js:

import { Component } from 'react';
import { setIdToken, setAccessToken } from '../utils/AuthService';

class Callback extends Component {

 componentDidMount() {
   setAccessToken();
   setIdToken();
   window.location.href = "/";
 }

 render() {
   return null;
 }
}

export default Callback;
Code language: JavaScript (javascript)

Add this piece of code to components/Nav.js:

import React, { Component } from 'react';
import { Link } from 'react-router';
import { login, logout, isLoggedIn } from '../utils/AuthService';
import '../App.css';

class Nav extends Component {

  render() {
    return (
      <nav className="navbar navbar-default">
        <div className="navbar-header">
          <Link className="navbar-brand" to="/">Miniflix</Link>
        </div>
        <ul className="nav navbar-nav">
          <li>
            <Link to="/">All Videos</Link>
          </li>
          <li>
            {
             ( isLoggedIn() ) ? <Link to="/upload">Upload Videos</Link> :  ''
            }
          </li>
        </ul>
        <ul className="nav navbar-nav navbar-right">
          <li>
           {
             (isLoggedIn()) ? ( <button className="btn btn-danger log" onClick={() => logout()}>Log out </button> ) : ( <button className="btn btn-info log" onClick={() => login()}>Log In</button> )
           }
          </li>
        </ul>
      </nav>
    );
  }
}
export default Nav;
Code language: JavaScript (javascript)

In the Nav component, you must have observed that we imported a css file. Open the App.css file and add this code here to it.

Add this piece of code to components/Display.js:

import React, { Component } from 'react';
import { Link } from 'react-router';
import Nav from './Nav';
import { isLoggedIn } from '../utils/AuthService';
import axios from 'axios';

class Display extends Component {

  render() {

    return (
      <div>
        <Nav />
        <h3 className="text-center"> Latest Videos on Miniflix </h3>
        <hr/>

        <div className="col-sm-12">

        </div>
      </div>
    );
  }
}

export default Display;
Code language: JavaScript (javascript)

Add this piece of code to components/Upload.js:

import React, { Component } from 'react';
import { Link } from 'react-router';
import Nav from './Nav';

class Upload extends Component {


  render() {

    return (
      <div>
        <Nav />
        <h3 className="text-center">Upload Your 20-second Video in a Jiffy</h3>
        <hr/>

        <div className="col-sm-12">
          <div className="jumbotron text-center">
            <button className="btn btn-lg btn-info"> Upload Video</button>
          </div>
        </div>
      </div>
    );
  }
}

export default Upload;
Code language: JavaScript (javascript)

Lastly, open up index.js and add replace it with the code here to set up your routes.

Now, when you run your app with npm start, you should have views like this:

MiniFlix1

MiniFlix2

We need a storage space for the videos our users will upload. Cloudinary is a cloud-based service that provides an end-to-end image and video management solution including uploads, storage, administration, manipulation and delivery. Head over to Cloudinary.com and create an account for free.

Let’s make use of Cloudinary’s Upload Widget. This widget allows you to upload videos or any type of file from your local computer, facebook, dropbox and Google Photos. Wow, very powerful. And the integration is seamless.

Go ahead and reference the cloudinary widget script in your index.html:

 <script src="//widget.cloudinary.com/global/all.js" type="text/javascript"></script>
Code language: HTML, XML (xml)

Note: You can add it just after the links.

Now in Upload.js, modify the code to look like this:

import React, { Component } from 'react';
import { Link } from 'react-router';
import Nav from './Nav';

class Upload extends Component {

  uploadWidget = () => {
    window.cloudinary.openUploadWidget(
      { cloud_name: 'cloud_name',
        upload_preset: '<unsigned-preset>',
        tags: ['miniflix'],
        sources: ['local', 'url', 'google_photos', 'facebook', 'image_search']
      },
      function(error, result) {
          console.log("This is the result of the last upload", result);
      });
  }

  render() {
    return (
      <div>
        <Nav />
        <h3 className="text-center">Upload Your 20-second Video in a Jiffy</h3>
        <hr/>

        <div className="col-sm-12">
          <div className="jumbotron text-center">
            <button onClick={this.uploadWidget} className="btn btn-lg btn-info"> Upload Video</button>
          </div>
        </div>
      </div>
    );
  }
}

export default Upload;
Code language: JavaScript (javascript)

In the code above, we added a third argument, tags. Cloudinary provides this for automatic video tagging. Every video that is uploaded to this app will be automatically tagged, miniflix. In addition, you can provide as many tags as you want. This feature is very useful when you want to search for videos too!

In the uploadWidget function, we called the cloudinary.openUploadWidget function and attached it to the “Upload Video” button. When the user clicks the button, it opens the widget. Replace the cloud_name & upload_preset values with your credentials from Cloudinary dashboard.

Sign in to your app, head over to the upload videos route and try uploading a video.

MiniFlix3

Upload Widget

MiniFlix4

Uploading the video…

It uploads the video straight to Cloudinary and returns a response object about the recently uploaded video that contains so many parameters such as the unique public_id, secure_url, url, original_filename, thumbnail_url, created_at, duration and so many others.

We need a dashboard to display all the videos uploaded for users to see at a glance. Here, we will make use of Cloudinary’s react component. Install it:

npm install cloudinary-react

Now, open up components/Display.js and modify the code to be this below:

import React, { Component } from 'react';
import { Link } from 'react-router';
import Nav from './Nav';
import { isLoggedIn } from '../utils/AuthService';
import { CloudinaryContext, Transformation, Video } from 'cloudinary-react';
import axios from 'axios';

class Display extends Component {

  state = { videos: [] };

  getVideos() {
    axios.get('https://res.cloudinary.com/unicodeveloper/video/list/miniflix.json')
          .then(res => {
            console.log(res.data.resources);
            this.setState({ videos: res.data.resources});
    });
  }

  componentDidMount() {
    this.getVideos();
  }

  render() {

    const { videos }  = this.state;

    return (
      <div>
        <Nav />
        <h3 className="text-center"> Latest Videos on Miniflix </h3>
        <hr/>

        <div className="col-sm-12">
          <CloudinaryContext cloudName="unicodeveloper">
            { videos.map((data, index) => (
                <div className="col-sm-4" key={index}>
                  <div className="embed-responsive embed-responsive-4by3">
                    <Video publicId={data.public_id} width="300" height="300" controls></Video>
                  </div>
                  <div> Created at {data.created_at} </div>

                </div>
              ))
            }
          </CloudinaryContext>
        </div>
      </div>
    );
  }
}

export default Display;
Code language: JavaScript (javascript)

In the getVideos code above, we take advantage of a very slick Cloudinary trick that helps grab all videos with a particular tag, when using just one tag. Check it out again:

https://res.cloudinary.com/unicodeveloper/video/list/miniflix.json
Code language: JavaScript (javascript)

So we if had a tag like vimeo, our url will end up with …/vimeo.json. So in the code below, we got all the videos and stored in the videos state.

axios.get('https://res.cloudinary.com/unicodeveloper/video/list/miniflix.json')
          .then(res => {
            console.log(res.data.resources);
            this.setState({ videos: res.data.resources});
    });
Code language: JavaScript (javascript)

The Cloudinary React SDK has 4 major components, Image, Video, Transformation and CloudinaryContext. We are interested in the Video and CloudinaryContext for now. Christian explained how these components work here.

In the render method, we simply just looped through the videos state and passed the public_id of each video into the Cloudinary Video component. The Video component does the job of resolving the public_id from Cloudinary, getting the video url, and displaying it using HTML5 video on the webpage. An added advantage is this: Cloudinary automatically determines the best video type for your browser. Furthermore, it allows the user have the best experience possible by choosing the best from the range of available video types and resolutions.

Run your app, and try to see the list of all videos. It should be similar to this:

MiniFlix5

You can also manipulate your videos on the fly, with the help of Cloudinary via the Transformation component.

Go ahead install the react twitter widget component:

npm install react-twitter-widgets

In the components/Display.js file, import the component at the top:

import { Share } from 'react-twitter-widgets'
…
…
Code language: JavaScript (javascript)

Now, add this piece of code just after the div that shows the time the video was created.

…
…
<Share url={`https://res.cloudinary.com/unicodeveloper/video/upload/${data.public_id}.mp4`} />
Code language: HTML, XML (xml)

Check your app again. It should be similar to this:

MiniFlix6

Now, try to tweet.

MiniFlix7

Simple! It’s really not that hard. The source code for this tutorial is on GitHub.

Our MVP is ready. Our entrepreneur. Now sit back, relax and watch your account become flooded with investor money! Wait a minute, there is a 90% probability that you’ll called to add more features to this app. Well, I think Cloudinary can still help you with more features such as:

  • Automatic Subtitles and translation
  • Video briefs – short video, based on few gif images that will extract from the uploaded video.
  • Automatic and/or manual video markers – marking specific locations in the video so the user can wait patiently to watch them, or jump directly to these points
  • Find Similar videos by automatic video tagging

Cloudinary provides many options for uploading, transforming and optimizing your videos. Feel free to dive in and explore them.

Back to top

Featured Post