So, I love the Dev.to community and more so I love how easy it is to create a blog. So, I was looking for a way to have my blogs that I publish on here go straight to my personal site. Thankfully, this was pretty easy due to dev's api and a couple react hooks. Here is how I implemented mine on my personal site.
Contents
- Direct to a specific blog article
- List your blog articles on a page
- Dynamically go to a blog article from a list
In a rush? Here is the Github Repo
For the sake of time I am going to make a few assumptions here:
You are familiar with React and React Hooks
You have a basic understanding of an API
You have at least one published article on Dev.to
1. Direct to a specific blog article
Let's start with creating a basic react app: npx create-react-app dev-blog
Once the app is done installing let's do our usual boilerplate clean up of the App.js
file. Here is mine:
function App() {
return (
<div>
Dev Blog
</div>
);
}
export default App;
Now let's take a look at the api docs here. While their api is still in beta, as you can see there are several ways you can retrieve data from the site. As you explore different paths and responses you may come across a few that are not correct or not returning but I have yet to come across one and overall am pretty pleased with their work.
The path I recommend is under articles
and named the A published article by path GET
call. Let's add a constant named blogPath to our code and we want to set that const to https://dev.to/api/articles/{Your UserName}/{Your Article ID}
Simply navigate to a blog post you want show to get that Article ID. Here is mine.
function App() {
const blogPath = 'https://dev.to/api/articles/simplymincy/test-post-2-2j8a`';
return (
<div>
Dev Blog
</div>
);
}
export default App;
The next thing we want to do is make a call with that const to get the response body. To do this we can use useState
and useEffect
and a package commonly used with react called axios
, which will be used to make our http requests. Let's go ahead and install that package npm i axios
.
After that installs let's use the useState hook and set it to be our article Body response.
import React, { useEffect, useState } from "react";
import axios from "axios";
function App() {
const blogPath = 'https://dev.to/api/articles/simplymincy/test-post-2-2j8a`';
const [articleBody, setArticleBody] = useState();
return (
<div>
Dev Blog
</div>
);
}
export default App;
To actually get that response we will need to now use useEffect and axios to make that call using the blogPath we created. It should look something like I have below with us checking for the response.data and setting that to our setArticleBody. Be sure to set a dependency here in your useEffect or this call will continuously run in the background. We only want this to update if there is a page load so we can set it dependent to the blogPath.
Axios makes it really easy to make requests. Feel free to check out their docs
import React, { useEffect, useState } from "react";
import axios from "axios";
function App() {
const blogPath = 'https://dev.to/api/articles/simplymincy/test-post-2-2j8a`';
const [articleBody, setArticleBody] = useState();
useEffect(() => {
axios.get(blogPath).then((response) => {
setArticleBody(response.data);
});
}, [blogPath]);
return (
<div>
Dev Blog
</div>
);
}
export default App;
Now at this point you would think we could replace the contents in the return with one of the properties we are getting in our response. I mean for sure "body_html" property should work right? Just use that and place that in the return. I am sure you know the problem with trying that but if you don't it is because that return is not real HTML
. It is jsx
. So, it will not do any formatting. So, instead let's go with the "body_markdown" property instead and to get the markdown formatting to show up correctly on our page we are going to use another package react-markdown
. This package converts your markdown code into code jsx
can understand and displays it properly. So, let's install that package and make those updates shall we:
function App() {
const blogPath = 'https://dev.to/api/articles/simplymincy/test-post-2-2j8a`';
const [articleBody, setArticleBody] = useState();
useEffect(() => {
axios.get(blogPath).then((response) => {
setArticleBody(response.data);
});
}, [blogPath]);
if (!articleBody) return null;
const markdown = articleBody.body_markdown;
return (
<div>
<ReactMarkdown>{markdown}</ReactMarkdown>
</div>
);
}
export default App;
I've also added a simple check on the body response to see if it exists and to return null if it doesn't. That way our app will not crash if that path no longer exists.
At this point you should be able to see the page loaded with your article. This is pretty basic but we can do some more dynamic things with this api.
2. List a collection of articles on a page
So, first thing we want to do here is make a new component. Let's call it Article.js
because this is where we will return... well our articles.
const Article = () => {
return ();
};
export default Article;
And since we did all that work already in our App.js
file. Let's just move that over to this Article.js
file.
import React, { useEffect, useState } from "react";
import axios from "axios";
import ReactMarkdown from "react-markdown";
const Article = () => {
const blogPath = `https://dev.to/api/articles/simplymincy/test-post-2-2j8a`;
const [articleBody, setArticleBody] = useState();
useEffect(() => {
axios.get(blogPath).then((response) => {
setArticleBody(response.data);
});
}, [blogPath]);
if (!articleBody) return null;
const markdown = articleBody.body_markdown;
return (
<div> Dev Blog
<ReactMarkdown>{markdown}</ReactMarkdown>
</div>
);
};
export default Article;
Our App.js
should be empty now and look like this:
import React from "react";
function App() {
return (
<div>
Empty App
</div>
);
}
export default App;
Before we do anything else to that file let's create our collection component Collection.js
with some of the same imports as before:
import React, { useEffect, useState } from "react";
import axios from "axios";
const Collection = () => {
return ();
};
export default Collection;
And we want to retrieve the data similar to how we did with the Article component but we want to use this url https://dev.to/api/articles?username={Your UserName}
to get the collection of articles instead.
import React, { useEffect, useState } from "react";
import axios from "axios";
const Collection = () => {
const collectionPath = `https://dev.to/api/articles?username=simplymincy`;
const [articleCollection, setArticleCollection] = useState();
useEffect(() => {
axios.get(collectionPath).then((response) => {
setArticleCollection(response.data);
});
}, [collectionPath]);
if (!articleCollection) return null;
return <div>Dev Collection</div>;
};
export default Collection;
Since we will be getting an array of data we will use the map()
method to display every element. It should look something like I have below where we are taking that articleCollection
and spitting out every article
(You can name that whatever you like) based on its id
.
const list = articleCollection.map((article) => (
<div key={article.id}>
Data we want to display here
</div>
));
return (
<div>Dev Collection
{list}
</div>
);
I also want to display an image and the title of the article so I will add a couple more but feel free to add whatever you like here based off the api.
import React, { useEffect, useState } from "react";
import axios from "axios";
const Collection = () => {
const collectionPath = `https://dev.to/api/articles?username=simplymincy`;
const [articleCollection, setArticleCollection] = useState();
useEffect(() => {
axios.get(collectionPath).then((response) => {
setArticleCollection(response.data);
});
}, [collectionPath]);
if (!articleCollection) return null;
const list = articleCollection.map((article) => (
<div key={article.id}>
<div>
<div>
<img src={article.social_image} alt="Article Images" />
</div>
<div>
<h4>{article.title}</h4>
</div>
</div>
</div>
));
return (
<div>
Dev Collection
{list}
</div>
);
};
export default Collection;
Let's now import the Collection component in our App.js
file
import React from "react";
import Collection from "./Collection";
function App() {
return (
<React.Fragment>
<Collection/>
</React.Fragment>
);
}
export default App;
We should now see a collection of our published articles on our page. Now the last thing we want to do (besides adding some css to have it more appeasing on our eyes) is have each article clickable and navigate to that particular article onClick. Let's do that now.
3. Navigate to a blog article from a list
For this we are going to use another common package that is used with React and that is react-router-dom
. So, let's install that and before we get the Collection
component ready to have a clickable element we need to go to our App.js
file and make a few changes. Right now our app is a single page app. Being that we will at the very least now have 2 pages we can now navigate, we need to set up some routes. This is a decent enough change so for this one I am going to show the fully updated App.js
file and then explain.
import React from "react";
import { BrowserRouter, Switch, Route } from "react-router-dom";
import Article from "./Article";
import Collection from "./Collection";
function App() {
return (
<React.Fragment>
<BrowserRouter basename={"/"}>
<Switch>
<Route
exact
path={`${process.env.PUBLIC_URL}/`}
component={Collection}
/>
<Route
exact
path={`${process.env.PUBLIC_URL}/Article/simplymincy/:path`}
component={Article}
/>
</Switch>
</BrowserRouter>
</React.Fragment>
);
}
export default App;
I am now using the react-router-dom
package in our most top level component because I want to set up the different routes I spoke of before. Hence why we are using the BrowserRouter
, Switch
, and Route
methods. The BrowserRouter
tag is used to push, replace, and pop the various return states we will now have in our app. Inside of that we set a Switch
tag which works very similiar to a switch statement meaning that it returns the first child it has that matches a location and as you can see we have 2 Route
tags. There are a lot of properties you can set for these and I highly recommend looking over the docs here, but for my example I am checking for an exact match on the path property and I am setting the components to the 2 components we created earlier. Looking closer you will see that I have the Collection
component set as my base url and the route with the Article
component with a more unique path. The /Article/simplymincy/
was simply what I chose here but you can have your path be anything as long as it is unique.
Last thing, I want to point out here is the :path
piece you see in that url. We use this here so that when we do eventually click on a link in our Collection
component we will pass it some parameters. We do that by importing react-router-dom
to our component and using the Link
method. Think of this as an <a>
tag. Here is the updated file now:
import React, { useEffect, useState } from "react";
import axios from "axios";
import { Link } from "react-router-dom";
const Collection = () => {
const collectionPath = `https://dev.to/api/articles?username=simplymincy`;
const [articleCollection, setArticleCollection] = useState();
useEffect(() => {
axios.get(collectionPath).then((response) => {
setArticleCollection(response.data);
});
}, [collectionPath]);
if (!articleCollection) return null;
const list = articleCollection.map((article) => (
<div key={article.id}>
<div>
<div>
<img src={article.social_image} alt="Article Images" />
</div>
<div>
<h4>{article.title}</h4>
</div>
</div>
<div>
<Link to={`/Article/simplymincy/${article.slug}`}>Read</Link>
</div>
</div>
));
return (
<div>
Dev Collection
{list}
</div>
);
};
export default Collection;
As you will notice in the Link
tag we are using the same url that we set in the Article route in the App.js
file. That is key, because remember we are using exact match here. If they do not match it will not return our Article
component. Which leads us to our last step. Remember when we set the :path
in our Article
route well we did that so we can set some value and pass it through our components freely. In this case we are going to use a value we get from our api call, which is slug
. This by the definition of the dev.to api is used to uniquely identify the articles users create. This is not only perfect to have our urls be unique but also will be able to get passed through so our Article
component can make a direct call on that path to gets its blog content. This update is rather small. We just want to import { useParams } from "react-router-dom";
and then replace our blogPath const with this:
const params = useParams();
const blogPath = `https://dev.to/api/articles/simplymincy/${params.path}`;
What we are doing here is passing in the parameters from the link we clicked on in the Collection component and we will set that slug to be the end of our blogPath. That way no matter what article we click on, it will know which one to retrieve its content for and display.
AND THATS IT!
At this point you should have all of your articles in a collection displaying and be able to click and display each article dynamically. With the API you can do a lot more such as GET and POST comments, likes, and other useful stats pertaining to your blog. I did not add any styling in here either. I leave that up to you. Here is an example of how I implemented mine.
Top comments (0)