DEV Community

loading...
Cover image for How to Embed Your Dev.to Blog in Your Personal Website

How to Embed Your Dev.to Blog in Your Personal Website

matjones profile image Mat Jones ・5 min read

I recently switched my blog over from Medium to DEV, one of the primary reasons being I wanted an easy way to show my full blog on my personal website, as well as on the platform I'm using.

In Medium's desperate quest for monetization, they've made it extremely difficult to embed your blog on your own website. There are several projects out there on GitHub that attempt to help do this, but none of them seem to work very well, and they're all somewhat lacking in the styles department.

DEV has an open API, making it much easier to embed your blog in your own website, with full custom markup and styles, so you can easily make it look exactly how you want. In this article, we'll take a look at just how easy this is to get set up.

We're going to be using TypeScript, but if you're not familiar with it or just aren't using it, you should still be able to follow along, just excluding the TypeScript interfaces.

API

I'm going to be using Superagent as my HTTP client, because I like the functional and declarative API, but you can easily swap this out for something else like Axios, or the browser Fetch API. The full API documentation for DEV can be found here.

Let's start by setting up some interfaces to type our responses with (if you're using vanilla JavaScript you can skip this step).

dev-to-article-meta.ts

export default interface DevToArticleMeta {
  type_of: string;
  id: number;
  title: string;
  description: string;
  readable_publish_date: string;
  slug: string;
  path: string;
  url: string;
  comments_count: string;
  public_reactions_count: string;
  collection_id?: number;
  published_timestamp: string;
  positive_reactions_count: string;
  cover_image: string;
  social_image: string;
  tag_list: Array<string>;
}
Enter fullscreen mode Exit fullscreen mode

dev-to-article.ts

import DevToArticleMeta from "interfaces/dev-to-article-meta";

export default interface DevToArticle extends DevToArticleMeta {
  body_html: string;
  body_markdown: string;
}
Enter fullscreen mode Exit fullscreen mode

Now let's set up a service to handle fetching our data. We'll need two methods:

  • async fetchArticles(): Promise<Array<DevToArticleMeta>>
  • async getArticle(slug: string): Promise<DevToArticle>

dev-to-service.ts

import DevToArticle from "interfaces/dev-to-article";
import DevToArticleMeta from "interfaces/dev-to-article-meta";
import superagent from "superagent";

// setup API endpoints and queries
const DEV_TO_USERNAME = "matjones"; // swap this for your username
const ARTICLES_API = "https://dev.to/api/articles";

// helper method to type responses
const parseResponse = <T>(response: any): T =>
  (typeof response === "string" ? JSON.parse(response) : response) as T;

const fetchArticles = async () => {
  // GET the endpoint
  const response = await superagent.get(ARTICLES_API)
          // and add the username query parameter
          .query({ username: DEV_TO_USERNAME });
  return parseResponse(response.body);
};

const getArticle = async (slug: string) => {
  // build the API endpoint URL, `slug` is the `slug`
  //property of one of your articles,
  // e.g. "protecting-your-privacy-online-3bmc"
  const endpoint = `${ARTICLES_API}/${DEV_TO_USERNAME}/${slug}`;
  const response = await superagent.get(endpoint);
  return parseResponse<DevToArticle>(response.body);
};

export const DevToService = {
  fetchArticles,
  getArticle,
};
Enter fullscreen mode Exit fullscreen mode

And that's pretty much it for our API layer. Nice and simple. 😎

The fetchArticles() function will fetch all of your published articles. If you want to implement pagination or a maximum number of results, you can add the page and per_page query parameters via the .query() method chained off of superagent.get().

Integrating Into a React App

While this is technically all you need to get the data needed to render your articles, we can make it a little easier to work with in a React app by adding some custom React hooks.

STOP: If you're not familiar with React hooks, go read up on hooks first, then come back. I'll wait.

We'll make a really simple hook for each of our service methods. Since useEffect callbacks can't be async themselves, we're going to be using an IIFE, but note that you could also use the trusty old Promise.then() syntax as well.

use-dev-to-articles.ts

import DevToArticleMeta from "interfaces/dev-to-article-meta";
import { useEffect, useState } from "react";
import { DevToService } from "utils/dev-to-service";

/**
* Fetch all of my published dev.to articles
* @param onError a callback which is invoked if the request fails
*/
export default function useDevToArticles(onError?: () => void) {
  const [loading, setLoading] = useState(true);
  const [articles, setArticles] = useState<Array<DevToArticleMeta>>([]);

  useEffect(() => {
    (async () => {
      try {
        setArticles(await DevToService.fetchArticles());
      } catch (e) {
        onError?.();
      }

      setLoading(false);
    })();
  }, [onError]);

  // return the array of articles, and the loading indicator
  return { articles, loading };
}
Enter fullscreen mode Exit fullscreen mode

use-dev-to-article.ts

import DevToArticle from "interfaces/dev-to-article";
import { useState, useEffect } from "react";
import { DevToService } from "utils/dev-to-service";

/**
* Get a specific article given the article's slug.
* @param slug the slug of the article to retrieve.
* @param onError a callback which is invoked if the request fails
*/
export default function useDevToArticle(slug: string, onError?: () => void) {
  const [loading, setLoading] = useState(true);
  const [article, setArticle] = useState<DevToArticle>();

  useEffect(() => {
    // this bit may not be necessary for you; I needed
    // it because I'm using Next.js server side rendering
    // so slug is `undefined` on the initial render
    // since I'm getting it from a route parameter
    // e.g. /blog/:slug
    if (slug == null || slug.length === 0) {
      return;
    }

    (async () => {
      try {
        setArticle(await DevToService.getArticle(slug));
      } catch (e) {
        onError?.();
      }

      setLoading(false);
    })();
  }, [onError, slug]);
  // return the article, and the loading indicator
  return { article, loading };
}
Enter fullscreen mode Exit fullscreen mode

Rendering an Article's Body

There are two ways you can render an article's body; using the body_html property or the body_markdown property. If your article does not contain any HTML elements (you're only using Markdown) and does not contain any DEV Liquid tags, you can use the body_markdown property and use something like react-markdown to render it.

However, if you're using Liquid tags, or you have HTML elements in your markdown (for example, I often use the <figcaption> element to add captions to images and code snippets), you'll need to render the body_html property. In React, this can be done like this:

<div dangerouslySetInnerHTML={{ __html: article.body_html }}/>
Enter fullscreen mode Exit fullscreen mode

Notice the dangerouslySetInnerHTML syntax; it is intentionally ugly to look at and difficult to type quickly, because doing this could potentially open your app to cross-site-scripting vulnerabilities. In general, you should only use dangerouslySetInnerHTML with trusted inputs (e.g. inputs that you control, not unknown end users).

Results

Using this approach, I was able to easily embed my DEV blog into my personal website. Clicking the comment or thumbs up icon opens the article on dev.to so that you can add reactions or comments.

Blog index page on my personal site

The /blog page on my personal website.

Blog article page on my personal site

A specific blog article on my personal website.

Discussion (1)

pic
Editor guide
Collapse
hrshmistry profile image
Harsh Mistry

thanks, really helpful