refactr.tech
with gatsby
Welcome!

A special thanks to...






About me
Question for you!

overview
- Static sites
- GatsbyJS
- Getting started
- Using Graphql
- Creating pages from data
- Ways we made refactr.tech better
Subtitle

What is a static site?




static site benefits
- Easier deployment
- Cheaper to host
- Static files respond to browser faster
gatsbyjs
- beyond your typical static site generator
- easily meet Progressive Web App standards
- uses ReactJS
- uses GraphQL to pull in data from various sources
- large ecosystem of plugins
- fast
React
A JavaScript library for building user interfaces.
It’s the framework that Gatsby uses to build pages and structure content.
Graphql
A query language that allows you to pull data into your website.
It’s the interface that Gatsby uses for managing site data.
Progressive web app
- Responsive
- Connectivity independent
- App-like-interactions
- Always up-to-date
- Safe
- Discoverable
- Re-engageable
- Installable
- Linkable



refactr.tech problems
- Hard for one person to manage
- Poor user experience if you have a bad internet connection
- Doesn't meet any standards of a progressive web app
getting started


Where do the files from the original refactr.tech site go?
Pages defined by react components

From HTML to JSX



Getting the data


module.exports = {
siteMetadata: {
title: "REFACTR.TECH"
},
plugins: [
{
resolve: `gatsby-source-airtable`,
options: {
apiKey: process.env.GATSBY_AIRTABLE_API_KEY,
tables: [
{
baseId: process.env.GATSBY_AIRTABLE_BASE_KEY,
tableName: `Speakers`,
queryName: `speakers`,
mapping: { headshot: `fileNode` }
},
{
baseId: process.env.GATSBY_AIRTABLE_BASE_KEY,
tableName: `Sessions`,
queryName: `sessions`,
tableLinks: ["Speakers"]
}
]
}
}
]
}
gatsby-config.js

How did I get data onto a page?

With our GraphQL query we get back a node of data for each speaker.
export default ({ data }) => {
return (
<SpeakerCardList items={data.allAirtable.edges} />
);
};
export const speakerPageQuery = graphql`
{
allAirtable(filter: { table: { eq: "Speakers" } }) {
edges {
node {
fields {
slug
}
data {
speaker_name
role
company
twitter
linkedIn
company_url
}
}
}
}
}
`;
Those nodes of data are now available on this page.
speakers.js
import React from "react";
import { SpeakerCard } from "./SpeakerCard";
export const SpeakerCardList = ({ items }) => {
return items.map(item => (
<SpeakerCard {...item.node.data} slug={item.node.fields.slug} />
));
};
import React from "react";
import Img from "gatsby-image";
export const SpeakerCard = ({
twitter,
linkedIn,
headshot,
speaker_name,
role,
company,
slug
}) => (
<div className="col-xl-3 col-lg-3 col-md-4 col-sm-12">
<div className="speakers xs-mb30">
<div className="spk-img">
<img className="img-fluid" alt="trainer-img" src={headshot[0].url}/>
<ul>
<li>
<a href={twitter}>
<i className="fa fa-twitter" />
</a>
</li>
<li>
<a href={linkedIn}>
<i className="fa fa-linkedin" />
</a>
</li>
</ul>
</div>
<div className="spk-info">
<h3>
<a href={slug} rel="noreferrer noopener" target="_blank">
{speaker_name}
</a>
</h3>
<p>{role}</p>
<h6>{company}</h6>
</div>
</div>
</div>
);


CREATING A PAGE FOR EACH SPEAKER
Gatsby is not limited to making pages from files like many static site generators.
Gatsby lets map your GraphQL query results to create pages.
What’s needed to create a page?
- Part 1: Build a “slug” for the page.
- Part 2: Get data we want to create the page with.
Part 1: Build the slug with onCreateNode
exports.onCreateNode = ({ node, actions }) => {
const { createNodeField } = actions;
if (node.internal.type === `Airtable` && node.table === `Speakers`) {
const slug = "/speakers/" + node.data.anchor
createNodeField({
node,
name: `slug`,
value: slug
});
}
};
{ id: '995475ec-7da4-538e-8232-1bf05043340c',
table: 'Speakers',
queryName: 'speakers',
internal:
{ type: 'Airtable',
contentDigest: '578c52ee6e014d486e694aa1e3f248f7',
owner: 'gatsby-source-airtable',
fieldOwners: { slug: 'default-site-plugin' } },
data:
{ speaker_name: 'Leonardo Graterol',
role: 'Web UI Engineer',
company: 'Globant',
twitter: 'https://twitter.com/pankas87',
linkedIn: 'https://www.linkedin.com/in/leonardo-graterol-24863223/',
bio: 'Leonardo is a fullstack(ish) web developer currently focused on frontend web development, working at the UI Engineering Studio of Globant.\n \n He has wide experience developing web sites and applications in several industries like e-commerce, digital marketing, GIS development companies and airlines.\n \n Leonardo is a fan of semantic HTML, good practices and web standards, and he believes in building a more accessible and inclusive online experience for everyone.',
featured: true,
session: [ 'recnq9psXs0kKF8X9' ],
anchor: 'leonardo-graterol',
order: 2,
Sessions_copy: [ 'recm05uYtbrDu7lvO' ],
session_track: [ 'Social Impact' ],
session_title:
[ 'Web Accessibility 101: Intersectional inclusion in the digital world' ],
url: 'https://refactr.tech/detail/speakers.html#leonardo-graterol',
session_url:
[ 'https://refactr.tech/detail/sessions.html#web-accessibility-101-intersectional-inclusion-in-' ],
session_anchor: [ 'web-accessibility-101-intersectional-inclusion-in-' ] },
fields: { slug: '/speakers/leonardo-graterol' }
}
We've extended a node to include a slug!
exports.createPages = ({ actions, graphql }) => {
const { createPage } = actions;
return graphql(
# GraphQL query for all nodes and new field on node goes here.
).then(result => {
# For each node of speaker data
result.data.speakers.edges.forEach(({ node }) => {
createPage({
# Build a path using our new field on the node
path: node.fields.slug,
# Use this page template
component: path.resolve(`./src/templates/the_speaker.js`),
# Data passed to context is available
# in page queries as GraphQL variables.
context: {
slug: node.fields.slug
}
});
});
});
};
creating a page



results so far...
Easier to manage.
Better experience if user is offline or has spotty internet access?
Qualifies as a progressive web app
Audit
Using Lighthouse in chrome dev tools

Lighthouse tests if your app:
- Can load in offline or flaky network conditions
- Is relatively fast
- Is served from a secure origin
- Uses certain accessibility best practices


with GatsbyJS
without GatsbyJS
How do we make refactr.tech available offline and resilient on a spotty internet connection?

How do we make refactr.tech faster?

gatsyby-config.js
module.exports = {
siteMetadata: {
title: "REFACTR.TECH"
},
plugins: [
{
resolve: `gatsby-source-filesystem`,
options: {
name: `assets`,
path: `${__dirname}/src/styles/assets/img/`
}
},
{
resolve: `gatsby-source-airtable`,
options: {
apiKey: process.env.GATSBY_AIRTABLE_API_KEY,
tables: [
{
baseId: process.env.GATSBY_AIRTABLE_BASE_KEY,
tableName: `Speakers`,
queryName: `speakers`,
mapping: { headshot: `fileNode` }
}
]
}
},
'gatsby-plugin-sharp',
'gatsby-transformer-sharp'
]
};



How do we give refactr.tech the attributes of a progressive web app?

Text
{
"name":"REFACTR.TECH",
"short_name":"REFACTR.TECH",
"start_url":"/",
"theme_color":"#6b37bf",
"background_color":"#6b37bf",
"display":"standalone",
"icons":[
{
"src":"icons/icon-48x48.png?v=cd08a2755a01e54cfc25c889378da60f",
"sizes":"48x48",
"type":"image/png"
},
]
}



Another audit


big improvement

without Gatsby
with Gatsby
results
Easier to manage
Better experience if user is offline or has spotty internet access
Qualifies as a progressive web app
More than doubled performance

resources
- Gatsbyjs.org (docs & tutorials)
- Gatsby Discord Community
- Gatsby channel on YouTube
thank you!
Rebuilding refactr.tech with gatsbyjs
By aliciabarrett
Rebuilding refactr.tech with gatsbyjs
- 785