DEV Community

loading...
Cover image for A dev.to component for your blog/portfolio

A dev.to component for your blog/portfolio

aswaff profile image Avery Swafford ・4 min read

When I was working on my portfolio I had already decided to start blogging on dev.to. With the simplicity and community I prefer this platform for blogging. With this in mind, I wanted my portfolio to have all of my blog articles, which would be on dev.to. I wanted this to be done dynamically.

I wanted a way to do with this without a rebuild of the site every time I posted a article. So I chose to make my component in the following way to fit my current wants and needs.

You can see the blog live on my site here.
You can also see the source code for this component here.

This component was made for Gatsby, however with a few tweaks it could be used with most any framework.

Our Blog List component

This will be for the list of the blog articles with a title, description, and some details about the blog post.

import React, { useState, useEffect } from 'react';
import { Router } from "@reach/router";
import { Link } from "gatsby";

import BlogTemplate from './blog.template';
import './blog.component.styles.css';

const axios = require('axios');

export default function Blog() {
    const [blogData, setBlogData] = useState(['']);

    useEffect(() => {
        async function fetchData() {
            await axios.get("https://dev.to/api/articles?username=aswaff")
                .then(data => setBlogData(data.data));

        } fetchData()
    }, []);


    const List = () => {
        return( 
        blogData.map(article =>
            <div className="articlewrapper">
                <Link to={`/blog/${article.slug}`} ><div className="articletitle">{article.title}</div>
                <div className="articledesc">{article.description}</div>
                <div className="article-details">{article.readable_publish_date}|
                                                🤍{article.public_reactions_count}
                                                🗨 {article.comments_count}</div></Link>


            </div>)
        )}

    const Article = () => {
        return(
            <BlogTemplate />
        )}

     return (                  
            <Router basepath="/blog">
                <List path="/" />
                <Article path="/:article" />
            </Router>
        )           
    }
Enter fullscreen mode Exit fullscreen mode

We are importing react hooks, Reach/router, Link, our blog template and CSS styling. Next we setting our blogData hook, which will hold our data after the fetch is done from the dev.to api.

After the initial render of the page, the fetch gets performed, so we set our blogData state.

We run .map on blogData and we get back data from each individual article. So when we pass our blog data into our divs, each article gets mapped, creating a list for us.

We have an Article function that returns our BlogTemplate we imported earlier, this is for our routing. Using Reach/router, we have "basepath" set for "/blog".

When we go to "/blog", the List function is rendered with our blogData.map. "/:article" dynamically routes us to whatever is after /blog/. We have set the article slugs to be links in our List function, so if the article slug is: "do-something-wrong-4f9c" it gets placed as a link in our article list as "blog/do-something-wrong-4f9c" and Reach/router routes us to the function Article, which returns us our BlogTemplate.

Blog Template component

import React, { useState, useEffect } from 'react';
import './blog.template.styles.css'

const BlogTemplate = () =>  {
  const [articleData, setArticleData] = useState('');
  const articleLink = window.location.pathname.slice(6)

  useEffect(() => {
    async function fetchArticleData() {
        await fetch(`https://dev.to/api/articles/aswaff/${articleLink}`)
            .then(response => response.json())
            .then(data => setArticleData(data))

    } fetchArticleData()
}, []);

  return (
    <div className="article-wrapper">
        <div className="article-image"><img src={articleData.cover_image} alt={articleData.name} /></div>
        <div className="article-title"><h1>{articleData.title}</h1></div>
        <div className="article-info">
          <img className="article-profile-pic" src={articleData.user?.profile_image_90} alt="profile" />
          <span className="article-arthur"><a href={articleData.url}>{articleData.user?.name}</a> </span>
          <span className="publish-date">— {articleData.readable_publish_date}</span></div>
        <div dangerouslySetInnerHTML={ {__html: articleData.body_html} } className="innerhtml"/>

    </div>
  )};

export default BlogTemplate;
Enter fullscreen mode Exit fullscreen mode

What we are doing with the blog template is taking the link of our current browser window, which should be based on the slug of our article on dev.to, and making another fetch to dev.to for the specific article data.

Using the "window.location.pathname" we get back a string of where the browser is currently at "/blog/do-something-wrong-4f9c". The slice() is to get us the actual article slug("do-something-wrong-4f9c") so that we can pass it into the dev.to api fetch, and get the article data.

Because anything after "/blog/" gets routed to our blog template, we can go to a specific article on our site without having to go to the blog list first.

The rest of the function is a simple return of the articleData we get, so we can format and style our page.

If you are on Gatsby, you will need a Gatsby-node.js config, that allows router to take control of the /blog/ route. You can find that in the source above. I also left my CSS styling in the Github repository. Feel free to add or change what you'd like!

Since this uses client side fetching and routing, you may need to modify the List component so it doesn't list all of the articles at once to improve performance. In my case I only have a few articles, so as of now I am fetching all of my articles. A feature that I plan to add later is a spinner that displays for a user so if a connection is slow, so they don't have a blank white screen while they wait for the page to render.

That's it! We have a working simple component that will always have our blog articles from dev.to on our portfolio or site.

Discussion (2)

pic
Editor guide
Collapse
braydentw profile image
Collapse
aswaff profile image