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.jsthen 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 - 
quotesfor holding the array of quotes andloadingfor toggling the Quotes component when the data is available or not. - In the 
fetchQuotesfunction, the API URL returns the quotes in pages (20 per page), then you usesetQuotesto save the quotes to the state and changesetLoadingto false. - If the 
loadingstate 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 theQuotescomponent. 
 - 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 
Quotescomponent accepts the propquotes- which is an array containing all the quotes received fromApp.js. - I created a 
QuoteCardcomponent that represents the structure of each quote. Each quotes are then rendered through theQuoteCardcomponent 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. - 
TwitterShareButtonwraps theTwitterIconcomponent 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. - 
TwitterIconalso 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,setQuotesandsetLoadingas 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,
- 
ReactPaginatewas imported from the package to display the navigation bar. - The 
breakLabel,previousLabel, andnextLabelrepresent the value of the break, next and previous buttons. - 
onPageChangecontains the function -handlePageClickto be called when a page is changed and also returns the exact value of the button clicked. - The function 
handlePageClickaccepts 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 
ClassNameenable you to style the buttons any way you want them. - 
pageCountaccepts the total number of pages as a prop fromApp.js. This value is required. - 
pageRangeDisplayedis 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)