March 25, 2018

Gatsby.js: in his house and in my house

Filed under: General Information,ResearchAndDevelopment — Ryan Wilcox @ 8:42 am

Introduction

I’ve been playing with Gatsby.js. Gatsby is a static site render that uses React and GraphQL to make neat sites that a modern 2016+ web developer is gonna love… then render these sites to plain ol’ static HTML.

So I started building something…

My goal: render my reading notes into a website

I’ve kept a wiki for a long time, and have been keeping notes in books for a very long time. Previously I would buy PDF copies of books and highlight and write notes in the PDF book. Since O’Reilly stopped selling PDF books I’ve started keeping notes in markdown files.

So, thus my goal: using gatsby to render my markdown notes into a pretty website.

Challenge One: create a simple site that renders my markdown files

This turns out to be well documented on the Gatsby site. This worked super well – I followed that almost exactly and made something kind of nice.

Challenge accomplished pretty easily!

Challenge Two: “Hmmm… what if I want to embed a Gatsby site into another site?”

I’ve worked on so many Rails apps where I end up writing a simple blog component because the site needed a technical or marketting blog in the same style as the rest of the site.

Likewise, wilcoxd.com is a statically rendered site, and I if I used the learning / Gatsby site as a replacement for the Wiki then I’d want the styles to match. Which probably means recreating a pretty old design on old tech.

Soo…. what if I could:

  1. Get Gatsby to render my Markdown files
  2. Pull away enough React code to get the markup I care about
  3. Present it on my page

Because my Gatsby pages come from Markdown there’s no fancy React components or liveness on the site: just rendered text.

How Gatsby renders markup pages

Gatsby generates both a rendered React component for the file / path AND also generates the HTML statically.

Given the following structure:

/src/pages/markdown_documents/learning_gatsby.md

And a path for that document at /learning/gatsby, the rendered path will be curl http://localhost:9000/learnings/gatsby/index.html

Problem solved? I can make an AJAX request for the index.html page, then cleverly insert it into the host page, right?

Inserting Markdown HTML content into a host page: simple insert

Again, I suspect this works because these pages are rendered markdown. No React components on my content pages: I just wanted the rendered HTML.

So I created a sample host page and busted out some jQuery.

In my gatsby template – what the Markdown walkthrough calls src/templates/blogTemplate.js, I gave a class to the returned div. <div className='learning-container'>.

In my host site I wrote the following Javascript function:

function htmlFromGatsby( url, callback ) {

    $.ajax( url, { complete: function( jqXHR, textStatus) {

        var info = $.parseHTML( jqXHR.responseText, document, { keepScripts: false } )
        var whereItIs = $(info).find("div.learning-container") // seek until we find the container in our learningTemplate
        callback(whereItIs[0].innerHTML) 
    }})
}

Tada! My AJAX function retrieves the statically rendered Gatsby page, breaks all the React stuff Gatsby added, goes into the container that has all my content, pulls it out, then adds it to the host document. Neat!!!

The index page of my notes site has a list of all my learnings. Because its Gatsby I construct that list with GraphQL and a custom React component that abstracts real HTML links.

My first tests started with a simple page of notes content. My next test was the index page: how would linking content work in a host website? And because the React Router takes care of all the page routing / location bar changing… well that’s a problem.

When I added my index page – chalked full of these links – my links just 404ed. OK, time to break out some more jQuery…

In my src/pages/index.html ( my Gatsby site) I added an ID to the div returned: div id="learning_site_index">, and my PostLink component I have create a <Link to={...} className="learning-link">.

Here’s the code I have on my host page:

$.ajax("http://localhost:8000/index.html", {complete: function( jqXHR, textStatus) {
    var info = $.parseHTML( jqXHR.responseText, document, { keepScripts: false } )

    var whereItIs = $(info).find("div#learning_site_index_content")
    $("#destination").html( whereItIs )

                    // gotta do it here, attaching to all, because can't use live events to override an event handled by the control itself. WD-rpw 04-24-2018
                    $("a.learning-link").on("click", function(evt) {
                        evt.preventDefault()
                        var defaultWhere = $(evt.target).attr("href") 

                        htmlFromGatsby("http://localhost:8000" + defaultWhere + "/index.html", function(outHtml) {
                          $("#destination").html( outHtml )
                        } )
                        return false
                    })
                }})

There: we display the index page, and clicking a link will fetch and display that content on the page.

Conclusions and Problems

So now I have a simple solution for embedding Gatsby content into other sites. There are some problems:

  1. It works for mostly static pages, like Markdown Gatsby rendered into HTML. Your fancy React components won’t work here.
  2. My sample code is 2006 style jQuery. There’s little (no?) SEO friendliness, the back button is broken, etc etc.

But it’s a great little proof of concept for when you need to make a simple blog on a simple site and want to get something out there quickly.

Wishes for the future

I understand the complexities involved, but I’d love to have a way to use my Gatsby created components as part of an already React-ified site. To be able to plop a <GatsbyRenderComponent> into my site somewhere and load data, or run a GraphQL query about something Gatsby knows about, inside and alongside my custom “host” site components would be super cool.