A quote sharing app is a web application that fetches a list of quotes and their authors from an API and allows you to share these quotes on your social media handles with just one click. By building this application, you will learn how to paginate data using React-paginate, and also integrate social media share buttons into your web applications using React-share.
Preview a live demo: Quotweet
React-share is a library that allows you to embed social media share buttons into your web application. It contains numerous social media icons and also supports custom icons.
React-paginate is a ReactJS component used to paginate data. It automatically creates a navigation bar used to move through the data when you pass it some props. It is a very flexible library that lets you style the navigation bar anyhow you want it using vanilla CSS or any CSS framework you prefer.
This article requires a basic understanding of working with APIs in React.js.
How to Validate Forms with React Hook Form in React Apps
David Asaolu γ» Apr 2 '22
Project Setup & Installations
To build this web application, you will have to install create-react-app, react-share and react-paginate.
π Open your terminal
π Install create-react-app, by running the code below.
npx create-react-app@latest quotweet
π Run the code below to install react-share
npm install react-share --save
π Add react-paginate by running the code below in your terminal.
npm install react-paginate --save
π Start the development server. If you are not using Tailwind CSS, you may skip to the next section
npm start
π Optional: Install Tailwind CSS by running the command below. Tailwind CSS is utility-first CSS framework for building mordern user interfaces.
npm install -D tailwindcss postcss autoprefixer
π Generate tailwind.config.js and postcss.config.js configuration files by running:
npx tailwindcss init -p
π Open tailwind.config.js
and copy the code below:
module.exports = {
content: ['./src/**/*.{js,jsx,ts,tsx}'],
theme: {
extend: {},
},
plugins: [],
};
π In the ./src/index.css
file, add Tailwind directive to your CSS:
@tailwind base;
@tailwind components;
@tailwind utilities;
Project Overview
π Open App.js
file and copy the code below:
function App() {
return (
<div>
<Header />
<Quotes />
<Pagination />
</div>
);
}
export default App;
From the code snippet above, I divided the web application into three components: headers, quotes, and a pagination bar.
π Create a components
folder and create each components in the folder.
Building the Header Component
This contains the title of the web application and other features you may want to add, such as sign-in, and sign-out buttons.
π Open Header.js
and create an menu bar for your web application. You can copy the code snippet below:
import React from 'react';
const Header = () => {
return (
<header className="w-full h-[10vh] bg-[#1DA1F2] flex flex-col items-center justify-center mb-8 sticky top-0 z-50">
<h3 className="text-2xl text-[#f7f7f7]">Quotweet</h3>
</header>
);
};
export default Header;
How to fetch data from the API and state management
The URL for the quotes is "https://api.quotable.io/quotes"
and the author's is "https://images.quotable.dev/profile/200/${authorSlug}.jpg"
. The author's URL accepts the authorSlug
as a parameter to get its image.
In order to make this tutorial simple and easy to follow, all state management will be done in
App.js
then the values will be passed down to other components asprops
.
Edit the App.js
as follows
import Pagination from './components/Pagination';
import Quotes from './components/Quotes';
import Header from './components/Header';
import { useState, useEffect } from 'react';
function App() {
const [quotes, setQuotes] = useState([]); //contains the quotes - array of objects
const [loading, setLoading] = useState(true); - //boolean value for checking whether the quotes are available
const fetchQuotes = () => {
fetch('https://api.quotable.io/quotes') //URL for fetching all the available quotes
.then((data) => data.json())
.then((res) => {
setQuotes(res.results);
setLoading(false)
});
};
useEffect(() => {
fetchQuotes();
}, []);
return (
<div className="w-full min-h-screen">
<Header />
{loading ? <p>Loading</p> : <Quotes quotes={quotes} />}
<Pagination/>
</div>
);
}
export default App;
- From the code snippet above,
- I created two states -
quotes
for holding the array of quotes andloading
for toggling the Quotes component when the data is available or not. - In the
fetchQuotes
function, the API URL returns the quotes in pages (20 per page), then you usesetQuotes
to save the quotes to the state and changesetLoading
to false. - If the
loading
state is true - meaning the content is not yet available, it displays loading to the user, then when it's available it displays the quotes via theQuotes
component.
- I created two states -
Building the Quotes Component
This component contains all the quotes gotten from the API.
import React from 'react';
import QuoteCard from './QuoteCard';
const Quotes = ({ quotes }) => {
return (
<main className="w-full flex item-center p-4 flex-wrap gap-6 justify-center max-w-[1500px] min-h-screen">
{quotes.map((quote) => (
<QuoteCard quote={quote} key={quote._id} />
))}
</main>
);
};
export default Quotes;
- From the code snippet above
- The
Quotes
component accepts the propquotes
- which is an array containing all the quotes received fromApp.js
. - I created a
QuoteCard
component that represents the structure of each quote. Each quotes are then rendered through theQuoteCard
component by mapping through the array of quotes.
- The
Building the QuoteCard component
This is the component that describes how the quotes should be displayed.
import React from 'react';
const QuoteCard = ({ quote }) => {
return (
<div className="w-[90%] bg-gray-50 sm:w-[300px] rounded-xl shadow hover:bg-gray-100 flex-col items-center justify-center p-4 text-center">
<div className="w-full flex items-center justify-center mb-6">
<img
src={`https://images.quotable.dev/profile/200/${quote.authorSlug}.jpg`}
alt={quote.author}
className="w-[100px] rounded-full"
/>
</div>
<div>
<h3>{quote.author}</h3>
<p className="opacity-40">{quote.content}</p>
</div>
</div>
);
};
export default QuoteCard;
- From the code snippet above,
- The component receives each quote then displays them according to the layout.
- The image tag also displays the image using the URL contained in the source attribute. The author's name and the quote are also displayed.
How to add Twitter share button using React-share
Since we've been able to fetch the quotes and their images successfully from the API, let's add the Twitter share button to the project.
Edit the QuoteCard
component
import React from 'react';
import { TwitterIcon, TwitterShareButton } from 'react-share'; //necessary import
const QuoteCard = ({ quote }) => {
return (
<div className="w-[90%] bg-gray-50 sm:w-[300px] rounded-xl shadow hover:bg-gray-100 flex-col items-center justify-center p-4 text-center">
<div className="w-full flex items-center justify-center mb-6">
<img
src={`https://images.quotable.dev/profile/200/${quote.authorSlug}.jpg`}
alt={quote.author}
className="w-[100px] rounded-full"
/>
</div>
<div>
<h3>{quote.author}</h3>
<p className="opacity-40">{quote.content}</p>
</div>
{/* ----- changes made ---- */}
<div className="icons w-full p-4 flex items-center justify-end">
<TwitterShareButton
title={`"${quote.content}" - ${quote.author}`}
url={'https://twitter.com'}
via={'Arshadayvid'}
hashtags={['30DaysOfCode', 'javascript']}
>
<TwitterIcon
size={32}
round={true}
className="opacity-40 cursor-pointer hover:opacity-100"
/>
</TwitterShareButton>
</div>
{/* ----- end of react-share ---- */}
</div>
);
};
export default QuoteCard;
- From the code snippet above,
- I imported the
TwitterIcon
- which provides the official icon for Twitter, and theTwitterShareButton
- which provides the share via Twitter functionality from react-share. -
TwitterShareButton
wraps theTwitterIcon
component and it also receives few props such as - title, url, via and hashtags. Title represents the content you want to share, URL is the twitter homepage link, via is optional and is used for Twitter mentions, and hashtags represents Twitter hashtags you want to add to each share. -
TwitterIcon
also accept props like size and round.
- I imported the
How to add Pagination using React-paginate
React-paginate is a flexible component that is very easy to use.
π Open your Pagination.js
file.
π Edit App.js
import Pagination from './components/Pagination';
import Quotes from './components/Quotes';
import Header from './components/Header';
import { useState, useEffect } from 'react';
function App() {
const [quotes, setQuotes] = useState([]);
const [totalPages, setTotalPages] = useState(null);
const [loading, setLoading] = useState(true);
const fetchQuoteTexts = () => {
fetch('https://api.quotable.io/quotes')
.then((data) => data.json())
.then((res) => {
setTotalPages(res.totalPages);
setQuotes(res.results);
setLoading(false);
});
};
useEffect(() => {
fetchQuoteTexts();
}, []);
return (
<div className="w-full min-h-screen">
<Header />
{loading ? <p>Loading</p> : <Quotes quotes={quotes} />}
<Pagination
totalPages={totalPages}
setQuotes={setQuotes}
setLoading={setLoading}
/>
</div>
);
}
export default App;
- From the code snippet above:
- I create a state to hold the total number of pages available, and once the data is available, the state's value changes to the total number of pages retrieved from the API.
- Pagination accepts
totalPages
,setQuotes
andsetLoading
as props.
π In the Pagination.js
file, we have the following code:
import React from 'react';
import ReactPaginate from 'react-paginate';
function Pagination({ totalPages, setQuotes, setLoading }) {
const handlePageClick = (data) => {
const pageNumber = data.selected + 1;
const fetchData = async () => {
fetch(`https://api.quotable.io/quotes?page=${pageNumber}`)
.then((data) => data.json())
.then((res) => {
setQuotes(res.results);
setLoading(false);
});
};
fetchData();
};
return (
<div className="w-full items-center justify-center mx-auto">
<ReactPaginate
breakLabel="..."
nextLabel=">>>"
previousLabel="<<<"
onPageChange={handlePageClick}
pageRangeDisplayed={2}
marginPagesDisplayed={1}
pageCount={totalPages}
renderOnZeroPageCount={null}
containerClassName="sm:py-4 sm:px-6 p-2 border-2 mt-8 flex items-center justify-center w-2/3 mx-auto mb-10 shadow-lg"
pageLinkClassName="sm:py-4 sm:px-6 p-2 bg-white"
previousLinkClassName="sm:py-4 sm:px-6 p-2 bg-white"
nextLinkClassName="sm:py-4 sm:px-6 p-2 bg-white"
breakLinkClassName="sm:py-4 sm:px-6 p-2 bg-white"
activeLinkClassName="bg-blue-100"
/>
</div>
);
}
export default Pagination;
- From the code snippet above,
-
ReactPaginate
was imported from the package to display the navigation bar. - The
breakLabel
,previousLabel
, andnextLabel
represent the value of the break, next and previous buttons. -
onPageChange
contains the function -handlePageClick
to be called when a page is changed and also returns the exact value of the button clicked. - The function
handlePageClick
accepts the index of the navigation icon clicked, then adds 1 to the value to fetch the data available on the API. - The props ending with
ClassName
enable you to style the buttons any way you want them. -
pageCount
accepts the total number of pages as a prop fromApp.js
. This value is required. -
pageRangeDisplayed
is the range of pages displayed. Visit the documentation for more information
-
Conclusion
React-share and React-paginate are two tiny libraries that you can add to your web applications when you need either a social media share feature or pagination.
This is a beginner-friendly project, you can extend it by:
- Adding authentication - (a sign-in and sign-up feature)
- Using another API, maybe a joke API where people can share jokes they found funny on their social media handles.
- Adding other social media sharing features
- Adding copy and paste feature
- Using any state management library - Redux, MobX, etc.
- Improving the design and user interface.
Thank you for reading this far!
Resources
API Documentation - https://github.com/lukePeavey/quotable
Image URL - https://images.quotable.dev/profile/200/${authorSlug}.jpg
Quotes URL - https://api.quotable.io/quotes
Live Demo: https://quotweet.vercel.app
Writer's Corner
Hi, I am open to freelance technical writing gigs and remote opportunities. Let's work together. π§: asaoludavid234@gmail.com
Top comments (0)