Asset(Image/Video) Picker with javascript(react/nextjs) on button click
Tags: webdev, javascript, beginners, programming
Published: 2023-03-23 07:54:24

Before we start i am going to put my social media links if you would like to follow me there
Instagram [](https://www.instagram.com/babou360)
Twitter [](https://www.twitter.com/babou360)

and also my upwork profile if you want to work with me 
[](https://www.upwork.com/freelancers/~011681294ec42b9963)


In this blog post i am going to demonstrate how to create an asset picker on button click.
Below is how the the ui of the picker looks like and how it works
{% youtube AzjW7_u3mdw %} 

First of all we are going to create our app with next js a react framework 

```
npx create-next-app assetpicker
```
make sure all the characters are in small letters because next js does not allow capital letters 
and during the creation of the app select yes for typescript on the terminal pop up because we are going to use typescript in this app
After app creation we are going to install 3 npm packages which are react-icons, classnames and sass

```
npm i react-icons classnames sass
```
After installation go to pages and delete index.tsx content but not the file itself and we are going to start from scratch 

we are going to have 4 items in our setState which are image, selecta, current and names 

```
 const [image, setImage] = useState<any>([])
 const [selecta, setSelecta] = useState<any>([])
 const [current, setCurrent] = useState(0)
 const [names, setNames] = useState<any>([])
```
image this is the actual **image **which is going to be displayed inside a div known as **selecta** and current is the current selected image which by default is the first one and names contains an array of names of all selected images this is going to help us to check if the image is already selected or not 

Apart from those we are going to have 3 functions addSelecta, onChange and deleteData

```
 const addSelecta = () => {
    let image1 = selecta.length
    if(selecta.length > image.length){
      alert('not allowed')
      return 
    }else if( selecta.length >= 5){
      alert('maximum images 5 reached')
      return 
    }else{
      selecta.push(`image${image1+1}`)
      setSelecta(selecta.map((item:any)=> item))
    }
  }
```
in this function we are adding a button to select image and that div is going to display a plus icon if there is no image selected and and image otherwise.
Every Time we click a button we are going to add an item named image plus selecta.length icreament so selecta item is going to be image0, image1 etc in an array 

```
 const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const one:any = e.target.files
    const two = URL.createObjectURL(one[0])
    if(names.includes(one[0].name)){
      alert('asset available')
      return 
    }else{
      image.push(two)
      names.push(one[0].name)
      setImage(image.map((item:any)=> item))
      setNames(names.map((item:any)=> item))
    }
  }
```
in in onchange function this is all about selecting assets but there is more to that.
we are going to have a constant value named **one **which is simply an array assignment of assets.
and the second constant value is **two** which is simply a url creation for selected files number one.
we are using URL.createObjectURL(one[0]) because we cannot display files as files files on the web unless they are BLOB so this method is simply a creation of a BLOB url

```
const deleteData = (image1:any, selecta1:any) => {
    const imageIndex = image.indexOf(image1)
    const selectaIndex = selecta.indexOf(selecta1)

    if(selecta.includes(selecta1)){
      selecta.splice(selectaIndex,1)
      image.splice(imageIndex,1)
      names.splice(image1.name,1)
      setSelecta(selecta.map((item:any)=> item))
      setImage(image.map((item:any)=> item))
      setNames(names.map((item:any)=> item))
    }
  }
```
The third function **deleteData** we are going to pass 2 items along with it the first one is **image **which is the image index of that selected image plus the selecta index of the selecta array.
Then we are going to get indexes of those two **imageIndex **and **selectaIndex **

and then we are going to check if the selecta array includes the selected selecta if true we are going to delete the selecta, image and name and after that we are going to set them with new array so as to update the ui because if we do not do that the changes will not be reflected on the ui

```
return (
    <div className={styles.home}>
      <div className={styles.item}>
        <div className={styles.hero}>
          <div className={styles.thumbs}>
            {
              image.map((item:any, index:number)=> (
                <div className={cx(styles.dot, current===index && styles.active)} onClick={()=> setCurrent(index)}></div>
              ))
            }
          </div>
          {
            image.length < 1
            ? null
            : <img src={image[current]} alt="" />
          }
        </div>
        <div className={styles.action}>
          <div className={styles.left}>
            {
              selecta.map((item:any, index:number)=> (
                <div className={styles.item}>
                  {
                    index+1 > image.length
                    ?<div className={styles.picker}>
                      <input type="file"
                      id='file'
                      style={{display:'none'}} 
                      onChange={e => onChange(e)}
                      />
                      <label htmlFor="file">
                        <BiPlus className={styles.icon} size={25}/>
                      </label>
                    </div>
                    : <img src={image[index]} alt=""  onClick={()=> deleteData(image[index], item)}/>
                  }
                </div>
              ))
            }
          </div>
          <div className={styles.right}>
            <div className={styles.add} onClick={() => addSelecta()}>
              <BiPlus className={styles.icon}/>
            </div>
          </div>
        </div>
      </div>
    </div>
  )
```
After all that we need to write our code we are going to have home as our main div and inside it there is item and inside item we are having hero section which is the big image.

```
 <div className={styles.hero}>
          <div className={styles.thumbs}>
            {
              image.map((item:any, index:number)=> (
                <div className={cx(styles.dot, current===index && styles.active)} onClick={()=> setCurrent(index)}></div>
              ))
            }
          </div>
          {
            image.length < 1
            ? null
            : <img src={image[current]} alt="" />
          }
        </div>
```
Inside the hero section we have thumbs which are the small dots indicating the selected image and unselected ones.

and then we have the action div with left and right divs the left is an array of selectas and the right one is the button to add selectas.
inside the selecta we are going to check if selecta has an image or not if not we are going to show a button to select the image 

here is the full code 

Index.tsx

```
import React, { useState } from 'react'
import styles from './home.module.scss'
import { BiPlus } from 'react-icons/bi'
import cx from 'classnames'

function Home() {
  const [image, setImage] = useState<any>([])
  const [selecta, setSelecta] = useState<any>([])
  const [current, setCurrent] = useState(0)
  const [names, setNames] = useState<any>([])

  const addSelecta = () => {
    let image1 = selecta.length
    if(selecta.length > image.length){
      alert('not allowed')
      return 
    }else if( selecta.length >= 5){
      alert('maximum images 5 reached')
      return 
    }else{
      selecta.push(`image${image1+1}`)
      setSelecta(selecta.map((item:any)=> item))
    }
  }

  const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const one:any = e.target.files
    const two = URL.createObjectURL(one[0])
    if(names.includes(one[0].name)){
      alert('asset available')
      return 
    }else{
      image.push(two)
      names.push(one[0].name)
      setImage(image.map((item:any)=> item))
      setNames(names.map((item:any)=> item))
    }
  }

  const deleteData = (image1:any, selecta1:any) => {
    const imageIndex = image.indexOf(image1)
    const selectaIndex = selecta.indexOf(selecta1)

    if(selecta.includes(selecta1)){
      selecta.splice(selectaIndex,1)
      image.splice(imageIndex,1)
      names.splice(image1.name,1)
      setSelecta(selecta.map((item:any)=> item))
      setImage(image.map((item:any)=> item))
      setNames(names.map((item:any)=> item))
    }
  }
  return (
    <div className={styles.home}>
      <div className={styles.item}>
        <div className={styles.hero}>
          <div className={styles.thumbs}>
            {
              image.map((item:any, index:number)=> (
                <div className={cx(styles.dot, current===index && styles.active)} onClick={()=> setCurrent(index)}></div>
              ))
            }
          </div>
          {
            image.length < 1
            ? null
            : <img src={image[current]} alt="" />
          }
        </div>
        <div className={styles.action}>
          <div className={styles.left}>
            {
              selecta.map((item:any, index:number)=> (
                <div className={styles.item}>
                  {
                    index+1 > image.length
                    ?<div className={styles.picker}>
                      <input type="file"
                      id='file'
                      style={{display:'none'}} 
                      onChange={e => onChange(e)}
                      />
                      <label htmlFor="file">
                        <BiPlus className={styles.icon} size={25}/>
                      </label>
                    </div>
                    : <img src={image[index]} alt=""  onClick={()=> deleteData(image[index], item)}/>
                  }
                </div>
              ))
            }
          </div>
          <div className={styles.right}>
            <div className={styles.add} onClick={() => addSelecta()}>
              <BiPlus className={styles.icon}/>
            </div>
          </div>
        </div>
      </div>
    </div>
  )
}

export default Home
```

home.module.scss

```
.home{
    width: 100vw;
    height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;

    .item{
        width: 50%;

        .hero{
            width: 100%;
            height: 400px;
            background: black;
            display: flex;
            align-items: center;
            justify-content: center;
            position: relative;

            img{
                width: 90%;
                height: 90%;
                object-fit: cover;
            }
            .thumbs{
                position: absolute;
                bottom: 0;
                display: flex;
                align-items: center;
                justify-content: center;

                .dot{
                    width: 10px;
                    height: 10px;
                    margin: 5px;
                    background: white;
                    cursor: pointer;

                    &.active{
                        background: crimson;
                    }
                }
            }
        }

        .action{
            width: 100%;
            display: flex;
            align-items: center;
            justify-content: space-between;
            margin: 10px 0;

            .left{
                width: 90%;
                display: flex;
                align-items: center;
                justify-content: flex-start;

                .item{
                    width: 100px;
                    height: 100px;
                    background: crimson;
                    margin-right: 10px;
                    display: flex;
                    align-items: center;
                    justify-content: center;
                    cursor: pointer;

                    img{
                        width: 100%;
                        height: 100%;
                        object-fit: cover;
                    }
                }
            }
            .right{
                width: 10%;

                .add{
                    background: crimson;
                    border-radius: 50%;
                    display: flex;
                    align-items: center;
                    justify-content: center;
                    width: 40px;
                    height: 40px;
                    cursor: pointer;

                    .icon{
                        color: white;
                    }
                }
            }
        }
    }
}
```