BUILDING A PORTFOLIO WITH Gatsby & GraphQL

@emmawedekind

servus,

Ich bin Emma.

Portfolios

Portfolio === website

what makes a portfolio...good?

good is subjective.

not everyone has the ability or privilege to create a portfolio.

you don't need to build an elaborate portfolio in order to get a job.

a portfolio is not a necessity.

a portfolio is one way you can tell the world who you are & what you like to build.

things your portfolio can do:

your portfolio can

tell people about you.

showcase your best work.

your portfolio can

exemplify your design & development skills.

your portfolio can

change over time.

your portfolio can

be a home for all of your content.

your portfolio can

ways to build a portfolio:

content management system (CMS)

html, css, & javascript

css frameworks

javascript libraries & frameworks

website builders

gatsby

static site generator

static sites

"a hybrid approach to web development which allow you to build a server-based website, yet pre-build it into static files for deployment."

- William vincent

https://wsvincent.com/what-is-a-static-site-generator/

React

vue

independent

golang

why would i use a static site generator?

fast

static site generators are

secure

static site generators are

affordable

static site generators are

reliable

static site generators are

c'mon...there must be some negatives.

static site generators

don't have real-time data...kind of

static site generators

don't have cms for non-developers.

how are static sites different from dynamic sites?

receiveOrder(🌮);
cookMeat(🐮);
chopVegetables(🍅);
makeGuacamole(🥑);
assembleTaco(🌮);
makeCustomerHappy(😍);

Dynamic sites

cookMeat(🐮);
chopVegetables(🍅);
makeGuacamole(🥑);
assembleTaco(🌮);

...
    
receiveOrder(🌮);
makeCustomerHappy(😍);

static sites

static sites do the heavy lifting as soon as the content changes.

let's code

but before we do...

gatsby is a static site generator based on react.

if you don't know react... that's okay!

i'm happy to answer questions after this talk.

gatsby is powered by graphql.

gatsby lets you pull data from anywhere.

Gatsby loads only the critical HTML, CSS, data, and JavaScript.

npm install -g gatsby-cli

// gatsby new <project-name> <starter-repo-link>
gatsby new my-portfolio && cd my-portfolio
gatsby develop
├── src
│   ├── components
│   │   ├── header.js
│   │   ├── image.js
│   │   ├── layout.css
│   │   ├── layout.js
│   │   └── seo.js
│   ├── images
│   │   └── gatsby-icon.png
│   └── pages
│       ├── 404.js
│       ├── index.js
│       └── page2.js
├── gatsby-browser.js
├── gatsby-config.js
├── gatsby-node.js
└── ...

HOME
podcasting
Speaking
Writing

├── ...
├── src
│   ├── components
│   ├── pages
│   │   ├── 404.js
│   │   ├── index.js
│   │   ├── podcasting.js
│   │   ├── speaking.js
│   │   └── writing.js
│   └── templates
└── ...
/podcasting
/speaking
/writing

pages

// pages/speaking.js
import React from "react"

import Layout from "../components/layout"

import SEO from "../components/seo"

const SpeakingPage = () => (
  <Layout>
    <SEO title="Speaking" />
    <h1>I speak about things</h1>
  </Layout>
)

export default SpeakingPage

layout

// components/layout.js

import React from "react"
import PropTypes from "prop-types"
import { useStaticQuery, graphql } from "gatsby"

import Nav from "../components/nav";

import "./layout.css"

...
// components/layout.js

...

const Layout = ({ children }) => {
  const data = useStaticQuery(graphql`
    query SiteTitleQuery {
      site {
        siteMetadata {
          title
        }
      }
    }
  `)
  
  ...
// components/layout.js

...

return (
    <div className='layout'>
      <Nav />
      <main className='main'>{children}</main>
    </div>
  )
}

Layout.propTypes = {
  children: PropTypes.node.isRequired,
}

export default Layout

// pages/podcasting.js
...
const PodcastingPage = () => (
  <Layout>
    <SEO title="Podcasting" />
    <h1>I podcast about things</h1>
  </Layout>
);

export default PodcastingPage;

navigation

// components/nav.js

import React from "react";
import { Link } from "gatsby";

import "./nav.css";

...

used to navigate between internal pages of a Gatsby site

link

  • pre-render content

  • state can be passed to linked page

  • custom active link styling

benefits of link

...
const Nav = () => (
    <nav className="nav">
        <h3 className="nav__title">My Portfolio</h3>
        <ul className="nav__list">
            <li>
	   	<Link 
  		to="/" className="nav__link" 
  		activeClassName="nav__link--active">
  		  Home
  		</Link>
	    </li>
            ...
        </ul>
    </nav>
)

export default Nav;
activeClassName="iAmActive"

activeStyle={{ textDecoration: 'underline' }}

set active link style

blog

You don't need to add a blog to your portfolio.

you don't need to blog.

but if you do want to add a blog...

gatsby makes creating a blog painless.

let's create a component for our "blog squares" on our writing page.

├── ...
├── src
│   └── components
│       ├── blogSquare.css
│       ├── blogSquare.js
│       ├── layout.css
│       ├── layout.js
│       ├── nav.css
│       ├── nav.js
│       └── seo.js
└── ...
// components/blogSquare.js

import React from 'react';
import { Link } from "gatsby";

import './blogSquare.css'

...
// components/blogSquare.js

...
const BlogSquare = ({ title, date, path, description }) => (
    <section className="blogSquare">
      <Link to={path} style={{
        textDecoration: 'none',
        color: '#4a4a4a'
      }}>
        <h2 className="blogSquare__title">{title}</h2>
        <p className="blogSquare__date">{date}</p>
        <p>{description}</p>
        <p style={{ fontSize: '.8em', textDecoration: 'underline' }}>Read more</p>
      </Link >
    </section >
  )
  
  export default BlogSquare

gatby doesn't have an opinion on which styling method you choose.

  • css-in-js

  • global stylesheets

  • modular stylesheets

how do we get the data for our blog square component?

GraphQL

But first...we need blog posts.

├── ...
├── src
│   └── pages
│       ├── 2019-08-24-cute-cats
│       │   └── index.md
│       └── 2019-08-26-five-tech-skills-to-master
│           └── index.md
└── ...
// index.md

---
path: '/cute-cats'
date: '2019-08-24'
title: 'Cute Cats'
author: 'Me'
description: 'Here are some cute cat photos!'
---
![Cat under a blanket](https://images.unsplash.com/)
# Other fun markdown
## Woohoo!

we need a way to structure each blog post.

templates

├── ...
├── src
│   ├── components
│   ├── pages
│   └── templates
│       └── blogPost.js
└── ...
// templates/blogPost.js

export default function Template({ data }) {
  const post = data.markdownRemark

  return (
    <Layout>
      <Link
        to="/writing"
        style={{ ... }}
      >
        Back to blogs
      </Link>
      <h1
        style={{ ... }}
      >
        {post.frontmatter.title}
      </h1>
      <h4
        style={{ ... }}
      >
        Posted by {post.frontmatter.author} on {post.frontmatter.date}
      </h4>
      <div dangerouslySetInnerHTML={{ __html: post.html }} />
    </Layout>
  )
}

the Gatsby starter uses the prop dangerouslySetInnerHTML to render markdown content.

react's replacement for using innerHTML in the DOM.

dangerously set inner html

we're dealing with static content; a cross-site scripting attack (XSS) is less of a threat...but is still a concern.

there are other ways to render blog content.

check out michal's blog post

https://michal.miskernik.sk/

we want to dynamically generate our blog post pages.

plugins

A Gatsby source plugin for sourcing data into your Gatsby application from your local filesystem.

gatsby-source-filesystem

Parses Markdown files using Remark.

gatsby-transformer-remark

yarn add gatsby-source-filesystem gatsby-transformer-remark 
// gatsby-config.js

plugins: [
		...
    `gatsby-transformer-remark`,
    {
      resolve: 'gatsby-source-filesystem',
      options: {
        path: `${__dirname}/src/pages`,
        name: 'pages',
      },
    },

get all blog posts

Each Markdown file is parsed into a node of type

markdown remark

http://localhost:8000/__graphql

Allows you to query your data with GraphQL in order to create pages.

createpages

1. Import our blog template: templates/blogPost.js

gatsby-node.js

2. Return a GraphQL query for all blog posts

3. If error...reject the promise

4. Dynamically create a blog page for each post

// gatsby-node.js

const path = require('path')

exports.createPages = ({ boundActionCreators, graphql }) => {
  const { createPage } = boundActionCreators

  const postTemplate = path.resolve('src/templates/blogPost.js')

...
// gatsby-node.js
...
return graphql(`
    {
      allMarkdownRemark {
        edges {
          node {
            html
            id
            frontmatter {
              path
              title
              date
              author
            }
          }
        }
      }
    }
  `).then(res => {...
// gatsby-node.js
...
if (res.errors) {
      return Promise.reject(res.errors)
    }

    res.data.allMarkdownRemark.edges.forEach(({ node }) => {
      createPage({
        path: node.frontmatter.path,
        component: postTemplate,
      })
    })
  })
}

rendering individual blog posts

// templates/blogPost.js

import React from 'react'
import { Link, graphql } from 'gatsby'

import Layout from '../components/layout'

export default function Template({ data }) {
  const post = data.markdownRemark
...
// templates/blogPost.js

export const postQuery = graphql`
query BlogPost($path: String!) {
  markdownRemark(frontmatter: { path: { eq: $path } }) {
    html
    frontmatter {
      date
      path
      title
    }
  }
}
`

congratulations!

you have a blog.

deployment

demo

to build a blog with gatsby...there's a shortcut.

gatsby blog starter

https://gatsby-starter-blog-demo.netlify.com/

gatsby new my-blog-starter https://github.com/gatsbyjs/gatsby-starter-blog

danke euch

@emmawedekind

Building a Portfolio With Gatsby & GraphQL

By Emma Wedekind

Building a Portfolio With Gatsby & GraphQL

  • 1,378