The author selected the COVID-19 Relief Fund to receive a donation as part of the Write for DOnations program.
According to the StackOverflow 2020 Developer Survey, React is one of the most popular JavaScript frameworks, and there are many reasons for this, like efficiently changing web app views with the Virtual DOM, using reusable, composable, and stateful components to increase scalability, and more. Beginner React developers often need experience putting their knowledge to use in real-life applications. This tutorial will give you that experience by showing you how to use React Hooks, use useState()
, and make API calls in React.
This article will discuss the step-by-step process of building a photo search application with React using the Unsplash API. Unsplash is currently one of the most used and popular photo search engines, and can be a great data provider when building projects and applications.
At the end of this tutorial, you’ll have a working application that uses React Hooks to query the Unsplash API. This project can also act as a boilerplate, since you can re-use the same programming logic and can use it as a base to build other projects involving API calls. Your photo search application will include a search bar and rendered results, as shown in the following:
If you would like to see the complete code, take a look at the DigitalOcean Community GitHub Repository.
In order to follow this guide:
You will need a free Unsplash account, which you can get at the official Unsplash website.
You will need a development environment running Node.js; this tutorial was tested on Node.js version 10.20.1 and npm version 6.14.4. To install this on macOS or Ubuntu 18.04, follow the steps in How to Install Node.js and Create a Local Development Environment on macOS or the Installing Using a PPA section of How To Install Node.js on Ubuntu 18.04.
You will also need a basic knowledge of JavaScript and HTML, which you can find in our How To Build a Website with HTML series and in How To Code in JavaScript. Basic knowledge of CSS would also be useful, which you can find at the Mozilla Developer Network.
In this step, you will make use of Create React App, which will get the initial project running without doing any manual configuration. In your project directory, run the following command.
- npx create-react-app react-photo-search
This command will create a folder named react-photo-search
with all the necessary files and configuration for a working React web aplication.
Use the cd
command to change directory and go inside this folder by running the following command:
- cd react-photo-search
Next, start the development server by running the following command:
- npm start
For information on this start script, check out How To Set Up a React Project with Create React App.
Next, head over to http://localhost:3000
in a web browser, or if you are running this from a remote server, http://your_domain:3000
.
You will find the React template:
Before moving further, you will have to clean the files. Create React App comes with sample code that is not needed and should be removed before building a project to ensure code maintainability.
You will now need to open another terminal since one is already taken up by npm start
.
Delete the default styling in index.css
by running the following command:
rm src/index.css
Next, open index.js
in a code editor with the following command:
- nano src/index.js
Since you have deleted index.css
, remove import './index.css';
from index.js
.
Your index.js
will be similar to this once you are done removing import ./index.css
from it.
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import * as serviceWorker from './serviceWorker';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
Save and exit the file.
Now delete the React logo by running the following command in the terminal:
- rm src/logo.svg
Open App.css
with the following command:
- nano src/App.css
Remove everything from App.css
, then save and exit the file. You will update this in Step 3 with your new styling.
Open src/App.js
with the following command:
- nano src/App.js
The next step is to remove import logo from './logo.svg';
and remove the JSX from the div
with the className="App"
in App.js
file. This will remove the HTML elements of the template.
Modify App.js
to look like this:
import React from 'react';
import './App.css';
function App() {
return (
<div className="App">
</div>
);
}
export default App;
Your http://localhost:3000
will be blank now.
You’ve now initialized a React app and cleaned the sample code from it. Next, you will create a new application in the Unsplash Developer dashboard and copy the Access Key
and Secret Key
of the application you just created to gain access to the Unsplash API.
In this section, you will apply for an Unsplash Developer Account, create a new application for this project, and copy the Access Key
and Secret Key
of this application to gain access to the Unsplash API. Since the Unsplash API is not a public API, you will need your own set of Unsplash API keys for this project.
Head over to Unsplash Developer Home and register as a developer. Since you already created an Unsplash Account this will be a quick process.
On the Unsplash Developer page, click the Register as a developer button.
Fill in your credentials to register.
After registering as a developer, you will be automatically redirected to your developer dashboard. Click on New Application.
You will be asked to accept the API Use and Guidelines. Click the checkboxes then the Accept terms button to proceed further:
You will then be prompted to give your Application information. Give your application an appropriate name and description, and click Create application.
With this, you have created an application and can now access your Access Key
and Secret Key
under the Keys section. Copy these keys to a secure location; you will need them later in your code.
Note that you will see a Demo tag after your application name:
This tag means your application is in development mode and the requests are limited to 50 per hour. For a personal project, this is more than enough, but you can also apply for production which will increase the requests limit to 5000 per hour. Do remember to follow the API Guidelines before applying.
In this section, you created an Unsplash API application and acquired the keys required for this project. For this project, you will use the official Unsplash JavaScript Library, unsplash-js
, to integrate the API with your app. You will install unsplash.js
and add CSS to style your project in the next step.
You will now install the unsplash-js
package as a dependency and add custom CSS to style your project. If at any point you get stuck, refer to the DigitalOcean Community Repository for this project.
To install unsplash-js
library with the npm package manager, run the following in your project directory:
- npm install unsplash-js
This is the only library that you will need to install to follow this tutorial; later on, you can experiment with different React User Interface libraries like React-Bootstrap, Semantic UI React, etc. You should add these libraries if, after following this tutorial, you want to tweak this project and change its layout.
Next, you will style your React app. Open App.css
by running the following command.
- nano src/App.css
This tutorial will discuss the CSS piece by piece.
First is the *
selector, which selects all the elements. Add the following code:
* {
box-sizing: border-box;
background-color: rgb(244, 244, 244);
color: #333;
font-size: 10px;
}
The box-sizing
property sets how the total width and height of an element is calculated and, in this case, it tells the browser to take border and padding into the calculation for an element’s width and height. The background color is set using background-color
and the value is rgb(244, 244, 244)
, which gives a pale white color to the background. color
sets the color of the text of the elements; here hexcode #333
is used, which is a dark shade of gray. font-size
sets the size of the font.
Next, add the .App
block, which selects the element with the className="App"
. By default the parent element (className="App"
) has some margin and padding, so the following code sets margin
and padding
of all four sides to 0
:
* {
box-sizing: border-box;
background-color: rgb(244, 244, 244);
color: #333;
font-size: 10px;
}
.App {
margin: 0;
padding: 0;
}
Next, add styling to the div
element with the className="container"
. This is the child element of the div
with className="App"
. Everything including title, form, button, and images will be included in this div
:
* {
box-sizing: border-box;
background-color: rgb(244, 244, 244);
color: #333;
font-size: 10px;
}
.App {
margin: 0;
padding: 0;
}
.container {
margin: 0 auto;
max-width: 1000px;
padding: 40px;
}
The margin
property is used to defined space around elements. margin
can be set for top
, right
, bottom
, and left
. If only one value is added, then this one value will set for all top
, right
, bottom
, and left
. If two values are added in margin
, then the first value will be set for top
and bottom
, and the second will be set for right
and left
.
According to margin: 0 auto;
, top
and bottom
have 0
margins while left
and right
have auto
. This auto
means that the browser will set the margin based on the container. An example to understand this will be if the parent element is 100px
and the child element is 50px
, then the left
, and right
margins will be 25px
, which will center the child element inside the parent element.
max-width
sets the maximum value of width
of the element, which in this case is 1000px
. If the content is larger than 1000px
, then the height
property of the element will change accordingly, else max-width
will have no effect.
As discussed above, margin
sets the space around the element while padding
sets the space between an element and its content. The earlier code means that the container
div
and the elements inside it will have 40px
of space between them from all four sides.
Next, add styling for the title of the application:
...
.container {
margin: 0 auto;
max-width: 1000px;
padding: 40px;
}
.title {
font-size: 4.4rem;
font-family: "Gill Sans", "Gill Sans MT", Calibri, "Trebuchet MS", sans-serif;
}
.title
corresponds to the title of your App, which is “React Photo Search”. Only two properties are set, which are font-size
and font-family
. Here, the rem
unit is used for the font-size
value. rem
values are relative to the root html
element, unlike em
values, which are relative to the parent element. Here the 4.4rem
means 44px
(4.4 x 10). This multiplication by 10px
is because you set the font size of all elements to 10px
using *
selector. font-family
specifies the font of the element. There are many values passed in the code to act as a fallback system; if the browser does not provide the first font, the next font is set.
Next is the .form
CSS block, which includes the form that will be used to search for images. This includes the input search field, button, and label.
...
.title {
font-size: 4.4rem;
font-family: "Gill Sans", "Gill Sans MT", Calibri, "Trebuchet MS", sans-serif;
}
.form {
display: grid;
}
Here, only the display
property is set. This property specifies the display behavior of the element. It can take different values like grid
, flex
, block
, inline
, etc. grid
displays an element as a block-level and renders the content according to the grid model.
Next is the .label
and the .input
CSS block:
...
.form {
display: grid;
}
.label {
font-size: 3rem;
margin-bottom: 1rem;
}
.input {
font-size: 1.6rem;
padding: 0.5rem 2rem;
line-height: 2.8rem;
border-radius: 20px;
background-color: white;
margin-bottom: 1rem;
}
We have already discussed font-size
, padding
, background-color
, and margin-bottom
, so let’s discuss line-height
and border-radius
. border-radius
defines the radius of the element’s corners. Here the value is set to 20px
, which will be used for all the four sides. Setting border-radius
to 50%
can make a square element into an oval. line-height
specified the height of the line, which is set to 2.8rem
or 28px
.
Next is the .button
CSS block, which styles the Search button:
...
.input {
font-size: 1.6rem;
padding: 0.5rem 2rem;
line-height: 2.8rem;
border-radius: 20px;
background-color: white;
margin-bottom: 1rem;
}
.button {
background-color: rgba(0, 0, 0, 0.75);
color: white;
padding: 1rem 2rem;
border: 1px solid rgba(0, 0, 0, 0.75);
border-radius: 20px;
font-size: 1.4rem;
cursor: pointer;
transition: background-color 250ms;
}
We have already discussed background-color
, color
, padding
, border-radius
, and font-size
. border
sets the style, width, and color of the border of an element. Here border
is used as a shorthand property for border-width
, border-style
, and border-color
. This code adds a solid black color border of 1px around the Search button. cursor
specifies the mouse cursor when pointing over an element.
Next is the :hover
selector, which is used on .button
.
...
.button {
background-color: rgba(0, 0, 0, 0.75);
color: white;
padding: 1rem 2rem;
border: 1px solid rgba(0, 0, 0, 0.75);
border-radius: 20px;
font-size: 1.4rem;
cursor: pointer;
transition: background-color 250ms;
}
.button:hover {
background-color: rgba(0, 0, 0, 0.85);
}
This means that when the mouse is hovered over the .button
element, the background color will change.
The next CSS block is .card-list
, which corresponds to the div
with className="card-list"
. This div
will display all the images inside it:
...
.button:hover {
background-color: rgba(0, 0, 0, 0.85);
}
.card-list {
column-count: 3;
}
column-count
divides the element into columns according to the value that is passed inside it. This code will divide the card-list
div
into three columns, and the images will be displayed within these three columns.
Next are the .card
and .card--image
CSS blocks. .card
refers to the individual div
with an image inside it, and .card--image
is the className
of this image:
...
.card-list {
column-count: 3;
}
.card {
margin-bottom: 1rem;
display: flex;
}
.card--image {
flex: 100%;
margin-top: 1rem;
border-radius: 10px;
}
We have already discussed margin
, display
, and border-radius
. In .card
, display
is set to flex
, which means the elements will behave like block elements, and the display will be set according to the flexbox model. By using the shorthand property flex:100%;
, you set the value for flex-grow
, flex-shrink
, and flex-basis
. You can read more about it at the Mozilla Developer Network.
The final CSS blocks involve media queries. By using the @media
rule, you can apply different styles for different media types/devices:
...
.card--image {
flex: 100%;
margin-top: 1rem;
border-radius: 10px;
}
@media (min-width: 768px) {
.form {
grid-template-columns: auto 1fr auto;
grid-gap: 1rem;
align-items: center;
}
.input {
margin-bottom: 0;
}
}
@media only screen and (max-width: 600px) {
.card-list {
column-count: 1;
}
}
According to this code, column-count
will change from 3
to 1
when the browser window is 600px
or less (applicable for most mobile devices). This used the max-width
property with the @media
rule. The code before that uses min-width
, which changes the style of the elements inside the @media
rule when the width is 768px
or more.
grid-template-columns
is used to specify columns in the grid model. The number of columns is equal to the number of values passed, which is three according to the code ( auto 1fr auto
). The first and third grid element’s size will be according to their container size or the content’s size. The second element will be given 1fr
(Fractional Unit), or the space left after the first and third elements have occupied according to their size. These three elements will be a camera emoji, the search input field, and the Search button. After the emoji and the button have taken the space according to their size, the rest of the area will go to the search input field, and it will change its width accordingly.
grid-gap: 1rem;
creates a space of 1rem
between two grid lines. align-items:center;
positions the items in the center of the container.
This finishes the styling of your application. Save and exit from src/App.css
. If you’d like to see the whole CSS file together, take a look at the GitHub repository for this code.
Now that you have installed the necessary dependency and added the custom CSS needed to style your project, you can move forward to the next section and design the UI or layout of the project.
In this section, you will design the UI of the project. This will include elements like a heading, label, input field, and button.
Open the src/App.js
file with the following command:
- nano src/App.js
To add a heading to your project, create a div
with the className="container"
inside your App.js
. Inside this div
add an h1
tag with the className="title"
and write React Photo Search
inside the tag. This will be the title heading:
import React from 'react';
import './App.css';
function App() {
return (
<div className="App">
<div className="container">
<h1 className="title">React Photo Search</h1>
</div>
</div>
);
}
Save and exit the file. In your browser, your app will now show your title:
Next, you will create a form that will take input from the user. This form will consist of an input text field and a submit button.
For this, create a new component named <SearchPhotos />
. It is not necessary to create a separate component, but as you develop this project, splitting code into components makes it easier to write and maintain code.
In the src
folder, create and open a new file called searchPhotos.js
with the following command:
- nano src/searchPhotos.js
Inside searchPhotos.js
, you export a functional component named <SearchPhotos />
:
import React from "react";
export default function SearchPhotos() {
return (
<>
</>
);
}
This is the basic structure of a functional component that you need to add to the searchPhotos.js
file. Save this file.
The next step is to import and use the SearchPhotos
component in App.js
.
In a new terminal window, open up App.js
:
- nano src/App.js
Add the following highlighted lines to App.js
:
import React from "react";
import "./App.css";
import SearchPhotos from "./searchPhotos"
function App() {
return (
<div className="App">
<div className="container">
<h1 className="title">React Photo Search</h1>
<SearchPhotos />
</div>
</div>
);
}
export default App;
Save this file.
To create the search form, you will use the form
tag and inside it, create an input field using the input
tag and a button using the button
tag.
Give the elements the className
of their respective tags. While you are doing this, add a label with a camera emoji inside it for styling:
...
export default function SearchPhotos() {
return (
<>
<form className="form">
<label className="label" htmlFor="query">
{" "}
📷
</label>
<input
type="text"
name="query"
className="input"
placeholder={`Try "dog" or "apple"`}
/>
<button type="submit" className="button">
Search
</button>
</form>
</>
);
}
First, you created a form
element with a className="form"
, and inside it a label
with a camera emoji. Then comes the input
element with attributes type="text"
, since the search query will be a string. The name="query"
attribute specifies the name of the input
element, className="input"
gives the element a class for styling, and the placeholder value for the search bar is set to Try "dog" or "apple"
. The final element in form
is a button
with the type="submit"
.
Save and exit the file. Your app will now have a search bar after the title:
Now that the UI of the app is complete, you can start working on the functionalities by first storing the input query from the user in the next section.
In this step, you will learn about states and React Hooks and then use them to store user input.
Now that you have constructed your application’s basic structure, we can discuss the React side of things. You have a form, but it doesn’t do anything yet, so the first thing to do is to take the input from the search bar and access it. You can do this with states.
States at their core are objects that are used to store the property values of components. Every time the state changes, the component re-renders. For this app, you need a state that will store the input or query from the search bar whenever the Search button is clicked.
One of the things that you may have noticed is that this project is using functional components. This allows you to use React Hooks to manage state. Hooks are functions that use React features like defining a state without writing a class. In this tutorial, you will make use of the useState()
Hook.
The first thing to do is import useState
inside your searchPhotos.js
file.
Open up the file:
- nano src/searchPhotos.js
Modify the first line of searchPhotos.js
file to the following:
import React, { useState } from "react";
export default function SearchPhotos() {
...
Next, you will implement useState()
. This is the syntax for the useState()
Hook:
useState(initialState)
useState()
returns the current state and a function commonly known as an updater function. To store these, you can use array destructuring:
const [query, setQuery] = useState(initialState);
In this example, query
stores the current state of the component, and setQuery
is a function that can be called to update the state. initialState
defines the initial state value; it can be a string, a number, an array, or an object depending on the use.
In your project, the input from the search bar is a string, so you will use an empty string as an initial value of the state.
In your searchPhotos.js
file, add the following line of code:
...
export default function SearchPhotos() {
const [query, setQuery] = useState("");
return (
<>
<form className="form">
<label className="label" htmlFor="query">
{" "}
📷
</label>
...
The next step is to set the value
of the input text field to query
and add an onChange()
event to it. This onChange()
event will have a function, inside which setQuery()
will be used to update the state. The input string is retrieved using e.target.value
:
...
<input
type="text"
name="query"
className="input"
placeholder={`Try "dog" or "apple"`}
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
...
Now, the state and the input field’s values are interlinked, and you can use this search query to search for the image.
You can view the input from the search bar inside the query
in real-time for testing purposes. Add console.log(query)
just after where you defined state:
...
export default function SearchPhotos() {
const [query, setQuery] = useState("");
console.log(query);
return (
<>
//
</>
);
}
Save the file.
You will now receive the input queries inside the console. You can open your console, using F12
in Chrome or Ctrl+Shift+K
in Firefox:
Now, searchPhotos.js
will look like this:
import React, { useState } from "react";
export default function SearchPhotos() {
const [query, setQuery] = useState("");
console.log(query);
return (
<>
<form className="form">
<label className="label" htmlFor="query">
{" "}
📷
</label>
<input
type="text"
name="query"
className="input"
placeholder={`Try "dog" or "apple"`}
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
<button type="submit" className="button">
Search
</button>
</form>
</>
);
}
This section discussed states and React Hooks and stored the user input in the input
field inside the query
state. In the next section, you will use this search query to search for the image and store the response inside another state.
You will now use the unsplash-js
library to search for images using the query from the input
field. The response will be stored inside another state named pics
.
You have already installed the unsplash-js
library, so import it in searchPhotos.js
file. You can also remove the console.log()
statement from the previous section:
import React, { useState } from "react";
import Unsplash, { toJson } from "unsplash-js";
...
toJson
is a helper function in the unsplash-js
library that is used to convert the response into JSON format. You can learn more about helper functions at the unsplash-js
GitHub page.
To use Unsplash in your app, make an instance of it using the new
keyword like this:
import React, { useState } from "react";
import Unsplash, { toJson } from "unsplash-js";
const unsplash = new Unsplash({
accessKey: "your_Access_Key",
});
Paste your Unsplash Access Key
to replace your_Access_Key
and you can now make API requests.
Warning: One should never share any access keys or Client ID’s for an API or any service. Potential bad actors can misuse them over the internet. In this scenario, they can make an unusual amount of requests that can be flagged as spam by your service provider, which can deactivate your application and account.
Now you will create an asynchronous function that will be triggered when clicking the Search button.
Just after where you defined state for query
, define an async
function:
...
export default function SearchPhotos() {
const [query, setQuery] = useState("");
const searchPhotos = async (e) => {
e.preventDefault();
console.log("Submitting the Form")
};
Here e.preventDefault()
stops the page from reloading whenever the Search button is clicked. You can pass this function in the onSubmit
event inside the form
tag. You can read more about this in the official React docs.
...
return (
<>
<form className="form" onSubmit={searchPhotos}>
...
Save the file. Now, if you click the Search button, you will receive Submitting the Form
in the console. You can remove this console.log()
after a successful response in the console.
Inside your searchPhotos()
function, you will use the Unsplash instance (unsplash
). You can use the search
method for searching the images. Here is the syntax for that:
search.photos(keyword, page, per_page, filters)
Here is the code to search for an image; add this code inside your searchPhotos()
function:
...
const searchPhotos = async (e) => {
e.preventDefault();
unsplash.search
.photos(query)
.then(toJson)
.then((json) => {
console.log(json);
});
};
...
First, you use unsplash.search
and then specify what to search for, which is in this case photos
. We can also search for users
or collections
. photos
takes the first required argument as the keyword to search for, which is query
; you can also specify the page, responses per page, image orientation, etc., through the optional arguments. For this tutorial, you only need the page
and per_page
arguments, limiting the response items you get from Unsplash.
Here are all the arguments that can be provided in photos
Argument | Type | Opt/Required | Default |
---|---|---|---|
keyword |
string | Required | |
page |
number | Optional | |
per_page |
number | Optional | 10 |
filters |
object | Optional | |
filters.orientation |
string | Optional | |
filters.collections |
array | Optional |
You can learn more about them at the unsplash-js
GitHub page.
You use the toJson
method to convert the response into JSON, and finally, console.log()
the response to test that API requests are made without any error. You will remove this console .log ()
in the next steps.
Save the file. Now open your console and click the Search button. You will find a response JSON like this:
{
"results": [{
"description": "Pink Wall Full of Dogs",
"alt_description": "litter of dogs fall in line beside wall",
"urls": {
"raw": "https://images.unsplash.com/photo-1529472119196-cb724127a98e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjE0MTQxN30",
"full": "https://images.unsplash.com/photo-1529472119196-cb724127a98e?ixlib=rb-1.2.1&q=85&fm=jpg&crop=entropy&cs=srgb&ixid=eyJhcHBfaWQiOjE0MTQxN30",
"regular": "https://images.unsplash.com/photo-1529472119196-cb724127a98e?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&ixid=eyJhcHBfaWQiOjE0MTQxN30",
"small": "https://images.unsplash.com/photo-1529472119196-cb724127a98e?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=400&fit=max&ixid=eyJhcHBfaWQiOjE0MTQxN30",
"thumb": "https://images.unsplash.com/photo-1529472119196-cb724127a98e?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=200&fit=max&ixid=eyJhcHBfaWQiOjE0MTQxN30"
},
...
}
You can remove or comment the console.log()
statement when you find a successful response from the Unsplash API, which means your code is working fine. This app will use the "urls"
field, since that will be the source of the image.
You’ve now used the query from the user to search for images when the Search button was clicked using the unsplash-js
library. Next, you will store the response inside another state named pics
and display the images by mapping the elements inside this state.
In this last section, you will store the response from Unsplash API inside another state named pics
and then map over the elements of this state to display the images on the webpage.
To show images, you need to access the response JSON, and for that, another state will be needed. The previous state query
stored queries from the user, which was used to make requests to the Unsplash API. This state pics
will store the image response you get from Unsplash API.
In searchPhotos.js
define another state like this:
...
const [query, setQuery] = useState("");
const [pics, setPics] = useState([]);
...
This state has been initialized with an empty array, and all the responses will be stored as an object inside this state. In other words, this is an array of objects.
To update this state with the JSON, you will use setPics
inside unsplash
API request:
...
unsplash.search
.photos(query, 1, 20)
.then(toJson)
.then((json) => {
setPics(json.results);
});
...
Now every time you search for a new query, this state will be updated accordingly.
Next, create a div
with the className="card-list"
just after where form
tags end:
...
<button type="submit" className="button">
Search
</button>
</form>
<div className="card-list">
</div>
</>
);
}
Inside this div
, you will map through the state and display the id
of the image:
...
<button type="submit" className="button">
Search
</button>
</form>
<div className="card-list">
{pics.map((pic) => pic.id )}
</div>
</>
);
}
You first use {}
to pass the JavaScript expression, inside which you use the .map()
method on your state.
Save your file. If you search now, you will see id
s associated with different objects on the webpage:
This is messy, but this also means your application is working.
Instead of displaying pic.id
, open up JSX inside the map
function and create a new div
with the className="card"
. This is going to be the container for each individual image:
...
<button type="submit" className="button">
Search
</button>
</form>
<div className="card-list">
{
pics.map((pic) => <div className="card"></div>);
}
</div>
</>
);
}
You can now display an image inside this div
:
...
<button type="submit" className="button">
Search
</button>
</form>
<div className="card-list">
{
pics.map((pic) =>
<div className="card">
<img
className="card--image"
alt={pic.alt_description}
src={pic.urls.full}
width="50%"
height="50%"
></img>
</div>);
}
</div>
</>
);
}
If you go back and see the response JSON, you will find a different kind of information. "urls"
contains the path to the image, so here pic.urls.full
is the actual path to the image and pic.alt_description
is the alt description of the picture.
There are different fields inside "urls"
that give different data, such as:
raw
: Actual raw image taken by a user.
full
: Raw image in .jpg
format.
regular
: Best for practical uses, width=1080px
.
small
: Perfect for slow internet speed, width=400px
.
thumb
: Thumbnail version of the image, width=200px
.
In this tutorial, you are using full
, but you can experiment with other types, too. You have also given a default height
and width
to the image.
Save your file.
Your application is almost finished; if you search now, you will be able to see your application in action. But there is still a small line of code left. If you search for your image and go to your console in the browser, you will see a warning.
Web consoleWarning: Each child in a list should have a unique "key" prop.
To fix this, pass a unique key
to every child using the id
of the image. This key
prop explicitly tells React the identity of each child in a list; this also prevents children from losing state between renders:
...
<div className="card-list">
{pics.map((pic) =>
<div className="card" key={pic.id}>
<img
className="card--image"
alt={pic.alt_description}
src={pic.urls.full}
width="50%"
height="50%"
></img>
</div>)};
</div>
</>
);
}
You can adjust the number of images you want to show by passing the corresponding argument to unsplash.search.photos()
.
Save and exit the file. You’ll now have a working photo search app:
In this section, you stored the response from Unsplash API inside the pics
state and displayed the images by mapping over the elements in pics
.
In this tutorial, you developed a React Photo Search app with the Unsplash API. In building the project, the tutorial discussed how to use React Hooks, query an API, and style a user interface.
There is much that can be done with this application to extend it. For example, you could add a Random button to display random images, create a checkbox to toggle between searching for photos or the users that posted them according to the user’s preference, add an infinite scroll to display more images, and more. You can also use the same concept and make other projects involving API requests, like the Hacker News API.
If you would like to look at more React tutorials, check out our React Topic page.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!
hello, it doesn’t work import Unsplash from ‘unsplash-js’; I installed successfully with: npm install unsplash-js, but the following error appears: Attempted import error: ‘unsplash-js’ does not contain a default export (imported as ‘Unsplash’).
Your help please
Could you elaborate a bit more about this part of the tutorial?
“You can adjust the number of images you want to show by passing the corresponding argument to unsplash.search.photos().”
This content is already published on Stephen Grider’s React Courses. Please don’t copy🙄.