loading...
Cover image for How to Build A Feed App With React and Tailwind CSS

How to Build A Feed App With React and Tailwind CSS

oyetoket profile image Oyetoke Toby ・5 min read

A feed app is a simple app that curates the latest news and articles all over the web and different sources.

So in this article, I will explain how to simply create one using React, Tailwind CSS and my Feed API (https://medrum.herokuapp.com). At the end of this article, we'll be able to build something similar to this - https://ey6n5.csb.app/

If you want a live and hands-on experience, you can follow the video below:

Subscribe to my channel here: https://bit.ly/oyetoketoby

If you are not able to watch the video, you can simply continue reading on below.

Prerequisites

  1. CRA (react)
  2. Axios (http)
  3. Tailwind CSS (CDN) (not necessary)

Setting Up Our Project

The first step here is to set up our react project using Create-React-App. If you don't have it installed, you can read about the installation process here.

To create our app, simply run the command below:

$ npx create-react-app feed-app

After successfully creating our CRA app, let's get into other setups.

Navigate to the src folder in the main directory and create:

  1. A components folder. Then in the components folder, create three files (sidebar.js, main.js, articles.js).
  2. a config.js file
$ mkdir components
$ touch config.js components/sidebar.js components/main.js components/articles.js

The next thing is to add the Tailwind CSS to your project. There are many ways to do this, but the easiest and fastest way is to use the CDN. You can head over here to get the CDN or simply use the one below:

<link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet">

Now in the public folder, open the index.html and update it with the Tailwind CSS.

Feed API - Understanding How Medrum API Works

Before we proceed into coding and the next phase of the project, let's try to understand how the Feed API works. Medrum is built using Python, BeautifulSoup, and Scrapy to scrape articles, news and other various things on the internet.

Medrum has two endpoints, articles and feeds. In articles, you can't really specify which source you want to read from, but you can from feeds.

Basically, to get latest articles, you can send a request to this endpoint - https://medrum.herokuapp.com/articles/ which would return something like below:

[{"title": "Juno Makes Writing Julia Awesome", "url": "https://towardsdatascience.com/juno-makes-writing-julia-awesome-f3e1baf92ea9", "comment_url": null, "ago": "3h", "date": "2020-02-02 04:06:09AM UTC"}, {"title": "Introducing Leya: The Meta-Statistical Lisp-like language", "url": "https://towardsdatascience.com/introducing-leya-the-meta-statistical-lisp-like-language-ef9012affbdb", "comment_url": null, "ago": "8h", "date": "2020-02-01 11:26:32PM UTC"}, {"title": "Transform Reality with Pandas", "url": "https://towardsdatascience.com/transform-reality-with-pandas-96f061628030", "comment_url": null, "ago": "10h", "date": "2020-02-01 09:34:26PM UTC"}, {"title": "Using Stringr and Regex to Extract Features from Textual, Alphanumeric  and Punctuation Data in R", "url": "https://towardsdatascience.com/using-stringr-and-regex-to-extract-features-from-textual-alphanumeric-and-punctuation-data-in-r-2565256c0a77", "comment_url": null, "ago": "13h", "date": "2020-02-01 06:31:13PM UTC"}, {"title": "Getting Browser User Permission with the Permissions API", "url": "https://levelup.gitconnected.com/getting-browser-user-permission-with-the-permissions-api-eafbc9c7f4d7", "comment_url": null, "ago": "15h", "date": "2020-02-01 04:03:06PM UTC"}, {"title": "Get More Out of Google Colab", "url": "https://medium.com/analytics-vidhya/get-more-out-of-google-colab-5bf9d9519a56", "comment_url": null, "ago": "16h", "date": "2020-02-01 03:34:54PM UTC"}]

You can filter the intervals of the articles scraped. It can either be latest, day, week, month. For instance, you can get all articles posted this week using - https://medrum.herokuapp.com/articles/?interval=week.

To be able to get a feed, you must specify a source id, which can be found here - https://medrum.herokuapp.com/sources/

To get dev.to feeds, you'd need to use the dev.to source id (5bbb1af8af62ff6841b4b26e) in the combination below:

https://medrum.herokuapp.com/feeds/?source=5bbb1af8af62ff6841b4b26e&page=1&sort=popular

You can filter the endpoints using page and sort (popular, latest).

Cool right and it's free to use.

You can support me on Patreon - https://www.patreon.com/oyetoketoby to continue building free APIs

Building Our Feed Application

Now that we have understood how the API works, let's see how to incorporate this into our app.

Open the config.js file:

export const SOURCES_URL = "https://medrum.herokuapp.com/sources/"
export const FEEDS_URL = "https://medrum.herokuapp.com/feeds"

This contains the two endpoints we'll be using in our application.

Now open the main.js file and paste the below:

import React, { useState, useEffect } from "react";

import Sidebar from "./sidebar";
import Articles from "./articles";
import { SOURCES_URL, FEEDS_URL } from "../config";

import axios from "axios";

function Main() {
  const [sources, setSources] = useState([]);
  const [articles, setArticles] = useState([]);
  const [source, setSource] = useState([]);


  const fetchSource = id => {
    setSource(id);
    setArticles([]);
    axios.get(`${FEEDS_URL}?source=${id}`).then(res => {
      setArticles(res.data);
    });
  };
  useEffect(() => {
    axios.get(SOURCES_URL).then(res => {
      setSources(res.data);
    });
    fetchSource("5718e53d7a84fb1901e05914");
  }, []);
  return (
    <div className="flex">
      <div className="w-1/4 bg-gray-500 p-3">
        <Sidebar source={source} sources={sources} fetchSource={fetchSource} />
      </div>
      <div className="w-3/4 bg-gray-400 p-3">
        <Articles articles={articles} />
      </div>
    </div>
  );
}

export default Main;

Here's what we did above:

  1. We imported our Sidebar and Articles components
  2. We added three useState hooks - sources, articles, source
  3. We created a function-fetchSource: This will be used in getting a source feeds.
  4. We added a useEffect hooks: This will get the available sources we can get their feeds and also loads a source feed by default.
  5. We passed the source, sources and fetchSource to the Sidebar component and articles to the Articles components as props

The next thing is to open the sidebar.js component:

import React from "react";

function Sidebar({ sources, fetchSource, source }) {
  if (!sources.length) return <p>Loading...</p>

  return (
    <>
      {sources.map((s, k) => {
        if (s.contentType!=="news") return null;
        return (
          <p
          key={k}
            className="mb-3"
style={{background: s.id===source ? "#ccc": "transparent"}}
            onClick={() => {
              fetchSource(s.id);
            }}
          >
            {s.name}
          </p>
        );      
      })}
    </>
  );
}

export default Sidebar;

The above is actually very easy to understand, but here's the breakdown:

  1. Show loading if the sources prop is empty
  2. Map through the sources prop, check if the contentType of the current source is news, then display the name, add an onClick event to call fetchSource with the source id as param and add a background if the current source id is the active source id.

The next thing now is to open the articles.js file:

import React from "react";

function Articles({ articles }) {
  if (!articles.length) return <p>Loading...</p>

  return (
    <>
      {articles.map((a, k) => {
        return (
          <p className="mb-4" key={k}>
            <a href={a.url} target="_blank">{a.title}</a>
          </p>
        );
      })}
    </>
  );
}

export default Articles;

In this we didn't do anything much, we only mapped through the articles prop to display all the feeds.

The last thing to do is to import our main.js component in app.js file:

import React from "react";
import "./styles.css";
import Main from "./components/main";

function App() {
  return (
    <div className="wrapper">
      <Main/>
    </div>
  );
}

That's it! If everything goes well, you should see something like below:

Screen Shot 2020-02-02 at 9.29.12 AM.png

That's all. You can check out a live demo here: https://ey6n5.csb.app/. You can check out the code on code sandbox here: https://codesandbox.io/s/boring-moore-ey6n5

Don't forget to share to others and if you want to support me here's my Patreon: https://www.patreon.com/oyetoketoby

Posted on by:

oyetoket profile

Oyetoke Toby

@oyetoket

Just a software engineer trying to make ends meet. My passion for Python and JavaScript is nothing to compare.

Discussion

pic
Editor guide