Building a custom blog doesn’t have to be hard, and you don’t have to do all of the hard work from scratch!
Gatsby, a static site generator for React, is an amazing tool that comes pre-configured with GraphQL and it allows you to easily get up and running.
Since Gatsby is a static site generator for React, you should have some React knowledge prior to taking this tutorial.
You should also be familiar with how to use the command line / terminal as we’ll be installing some packages with npm.
We need to install the Gatsby CLI (command-line-interface) to build new Gatsby projects.
To install the Gatsby CLI tool, run the following npm
command:
npm install -g gatsby-cli
Gatsby has a series of starter files called Gatsby Starters which allow you to quickly spin up different types of Gatsby sites.
Gatsby provides a starter blog which you can use to quickly get up-and-running.
If you aren’t familiar with Gatsby or GraphQL, I recommend following the in-depth tutorial to learn the in-and-outs.
To build a blog with the blog starter, simply run:
gatsby new <<my-blog-name>> https://github.com/gatsbyjs/gatsby-starter-blog
Then add your markdown blog files and customize to your heart’s content.
Let’s walk through the steps of building a Gatsby blog from scratch using the default starter.
First, create your starter project by running
gatsby new <<my-blog-name>> && cd <<my-blog-name>>
gatsby develop
When you open localhost:8000
in your browser, you will see the Gatsby default application.
Let’s go ahead and remove all of the boilerplate. We will leave the current file structure inside of the src/
directory but remove all of the files inside.
rm -rf src/**/*.*
Since Gatsby is a static site generator for React, you can write simple React components, like you would do with create react app.
Here is the current architecture of our application:
components/
: Contains all of your React components (i.e. navigation).pages/
: Contains all pages with unique routes: any JavaScript file located in this directory will be accessible through its own URLmy-website/<<page-name>>
images/
: Contains all image assets for our project.
Let’s go ahead and add some of the files that we’ll need to build our blog.
Our blog will have four pages:
- Home
- About
- Blog
- Contact
Let’s create a JavaScript file for each of these pages inside of the pages
directory:
index.js
about.js
blog.js
contact.js
Since we also removed all of the images from our project, we need to remove the reference to gatsby-icon
to fix our development server.
Inside gatsby-config.js
, remove the icon from the options
object.
// delete me
icon: `src/images/gatsby-icon.png`
To check whether everything is working as expected, let’s have index.js
in the pages/
directory return some simple JSX.
// pages/index.js import React from "react" const Home = () => ( <div> <h1>Home</h1> </div> );
export default Home
When we restart our development server and head to our browser, we should see this:
Let’s add similar JSX into the other three page components:
// pages/about.js import React from "react"
const About = () => ( <div> <h1>About</h1> </div> );
export default About
// pages/blog.js
import React from "react"
const Blog = () => (
<div>
<h1>Blog</h1>
</div>
);
export default Blog
// pages/contact.js
import React from "react"
const Contact = () => (
<div>
<h1>Contact</h1>
</div>
);
export default Contact
If we head back to the browser and add a /about
to the end of our localhost
URL, we should see the about page. Likewise this will work for /blog
and /contact
.
So all of our pages are rendering, but wouldn’t it be nice if we had a navigation component we could use to switch between page views? Let’s build one!
First let’s create two new files in the components/
directory: Nav.js
and nav.css
.
Inside Nav.js
add the following code:
// Nav.js import React from "react"
const Nav = () => ( <nav> <ul> <li> <a href="/">Home</a> </li> <li> <a href="/about">About</a> </li> <li> <a href="/blog">Blog</a> </li> <li> <a href="/contact">Contact</a> </li> </ul> </nav> ) export default Nav
Since we want the navigation bar on every single page, we could import it to each individual page and render it, however there’s an easier way.
We can use a <Layout>
component to ensure our navigation is rendered on each page, without having to manually import and render it for each one.
This is what our <Layout>
component will look like:
The navigation bar will sit at the top of the page, and all of the page content will be rendered in a <main>
element beneath.
Inside components/
create Layout.js
.
First, let’s import React, Prop Types, and our Nav component:
// Layout.js import React from "react" import PropTypes from "prop-types"
import Nav from "./Nav"
Next, we’ll create a stateless functional React component, passing children
as a prop.
// Layout.js const Layout = ({ children }) => ( <> <Nav /> <main>{children}</main> </> );
export default Layout;
We also want to ensure we’re passing JSX to the <Layout>
component, so we’ll use PropTypes to enforce that.
// Layout.js import React from "react" import PropTypes from "prop-types"
import Nav from "./Nav"
const Layout = ({ children }) => ( <> <Nav /> <main>{children}</main> </> );
Layout.propTypes = { children: PropTypes.node.isRequired }
export default Layout;
Let’s see if our <Layout>
component works.
Back in index.js
, let’s import our <Layout>
component and render it around our page content.
// index.js import React from "react" import Layout from "../components/Layout"
const Home = () => ( <Layout> <h1>Home</h1> </Layout> );
export default Home;
If we head over to our browser we should see the navigation appearing above our page title:
Let’s add the <Layout>
component to the other three pages.
// pages/about.js import React from "react" import Layout from "../components/Layout"
const About = () => ( <Layout> <h1>About</h1> </Layout> );
export default About
// pages/blog.js
import React from "react"
import Layout from "../components/Layout"
const Blog = () => (
<Layout>
<h1>Blog</h1>
</Layout>
);
export default Blog
// pages/contact.js
import React from "react"
import Layout from "../components/Layout"
const Contact = () => (
<Layout>
<h1>Contact</h1>
</Layout>
);
export default Contact
If we head back to the browser, we can now click each navigation item and see it’s respective page content.
And while this works, you’ll notice a re-render on each page. This is because we’re using the <a>
tag to link between pages, and this forces a re-render.
As a solution, Gatsby provides a <Link>
component to handle page routing.
Let’s head back to Nav.js
and fix the page routing.
First let’s import Link.
// Nav.js
import React from "react";
import { Link } from "gatsby";
Next, let’s replace all <a>
tags with <Link>
and change the href=
attributes to to=
attributes.
// Nav.js import React from "react"; import { Link } from "gatsby";
const Nav = () => ( <nav> <ul> <li> <Link to="/">Home</Link> </li> <li> <Link to="/about">About</Link> </li> <li> <Link to="/blog">Blog</Link> </li> <li> <Link to="/contact">Contact<Link> </li> </ul> </nav> ) export default Nav
Switching back to the browser, the page routing should be instantaneous when we click a navigation item.
Now that our app works, let’s add a bit of styling to make it look nicer.
Gatsby makes it simple to add specific styles when a navigation link is active.
There are two ways to add styles to an active link:
activeStyle
: Uses inline CSS-in-JS to style an element when active.
<Link activeStyle={{ backgroundColor: 'red' }}>My link</Link>
activeClassName
: Gives the link element a class name when active.
<Link activeClassName="active-link">My link</Link>
I generally use activeClassName
if I have multiple links, however we’ll use activeStyle
here to demonstrate CSS-in-JS.
I’ve also added some className
attributes to the JSX code which we’ll use to style the rest of our navigation.
// Nav.js import React from "react" import { Link } from "gatsby"
import "./nav.css"
const Nav = () => ( <nav className="nav"> <ul className="nav-list"> <li className="nav-list-item"> <Link
activeStyle={{ borderBottom: "2px solid #a64ac9" }} to="/" > Home </Link> </li> <li className="nav-list-item"> <Link
activeStyle={{ borderBottom: "2px solid #a64ac9" }}
to="/about" > About </Link> </li> <li className="nav-list-item"> <Link
activeStyle={{ borderBottom: "2px solid #a64ac9" }}
to="/blog" > Blog </Link> </li> <li className="nav-list-item"> <Link
activeStyle={{ borderBottom: "2px solid #a64ac9" }}
to="/contact" > Contact </Link> </li> </ul> </nav> )
export default Nav
Now each link, when selected, will have an underline of 2px
.
Let’s create a nav.css
file in the components/
directory and add the following code.
/* nav.css */ .nav { padding: 24px; }
.nav-list { list-style: none; display: flex; margin: 0; padding: 0; }
.nav-list-item { margin-right: 24px; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif; font-size: 1.5em; }
.nav-list a { color: #a64ac9; text-decoration: none; border-bottom: 2px transparent; transition: border 0.1s linear; }
.nav-list a:hover, .nav-list a:focus { border-bottom: 2px solid #a64ac9; }
Don’t forget to import nav.css
in Nav.js
:
// Nav.js
...
import "./nav.css";
...
Now let’s add some styling to Layout.js
. Create layout.css
in the components/
directory.
/* layout.css */ html, body { padding: 0; margin: 0; overflow-x: hidden; }
.main { padding: 24px; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif; background-color: #a64ac9; height: 100vh; color: #ffffff; }
.main h1 { font-size: 5em; }
.main p { font-size: 2em; }
Import the CSS file into Layout.js
and add a class name of layout
to the outer <div>
element and a class name of main
to the <main>
element.
// Layout.js
...
import "./layout.css"
...
<Layout className="layout">
<Nav />
<main className="main">{children}</main>
</Layout>
...
Lastly I’m going to add some filler content to index.js
, about.js
, and contact.js
. I added 5 paragraphs with lorem ipsum text to each of the three pages.
// index.js, about.js, contact.js
...
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Ea dignissimos
aut consequuntur aspernatur corrupti ratione, odit similique tenetur
accusantium, est nostrum esse minus iure voluptatum nihil cumque
blanditiis non? Odit.
</p>
...
Your site should now look much better!
Now it’s time to add some blogs!
Inside of the pages/
directory, create three folders: 2020-01-01-my-first-blog
, 2020-02-14-valentines-day
, 2020-04-01-april-fools
.
Inside each of these folders, add an index.md
file with the following structure:
--- path: '/my-first-blog' date: '2020-01-01' title: 'My First Blog' author: 'Emma Wedekind' description: 'This is my very first blog of 2020!' ---
Here is my main content It is very interesting.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Ea dignissimos aut consequuntur aspernatur corrupti ratione, odit similique tenetur accusantium, est nostrum esse minus iure voluptatum nihil cumque blanditiis non? Odit.
Everything between the three hyphens is called frontmatter. Frontmatter is essentially metadata for your blog post.
For each blog post, add some frontmatter, containing the following data:
path
: The URL path to your blogdate
: The date of publishtitle
: The blog post titleauthor
: The blog post authordescription
: The blog post description
Everything after the closing hyphens is the main body of the blog post. You can add whatever you’d like here.
Add markdown content for each of our three blog posts.
Now that we have markdown files, we want to render them on our blog.js
page.
We first need to install two package dependencies:
In your terminal, run the following command:
yarn add gatsby-transformer-remark
Then, in gatsby-config.js
, add gatsby-transformer-remark
to the list of plugins.
// gatsby-config.js
plugins: [
...
`gatsby-transformer-remark`,
...
]
We also need to add another plugin for gatsby-source-filesystem
to tell GraphQL where to find our blog posts: our pages/
directory.
// gatsby-config.js
{
plugins: [
...
{
resolve: `gatsby-source-filesystem`,
options: {
name: `pages`,
path: `${__dirname}/src/pages`,
},
...
]
Restart your development server, then head over to http://localhost:8000/___graphql
.
Gatsby comes pre-configured with GraphQL which means we have access to GraphiQL.
To get a list of all blog posts, we’ll use the AllMarkdownRemark
plugin. Select the following options in the Explorer panel:
AllMarkdownRemark > edges > node > frontmatter > date title
Then press the play button.
You should see your blog post data in the right-hand panel.
Copy this GraphQL query and head over to blog.js
.
First, import graphql
from gatsby
.
// blog.js
...
import { graphql } from "gatsby"
...
Then, after the blog export, add the following code, pasting in the GraphQL query we just copied from GraphiQL where it says <<Your code here>>
export const AllBlogsQuery = graphql`
<<your code here>>
`
My query looks like this (I added description
path
, and author
to the list of data to retrieve from the frontmatter).
// blog.js
export const AllBlogsQuery = graphql`
query AllBlogPosts {
allMarkdownRemark {
edges {
node {
frontmatter {
date
title
description
author
path
}
}
}
}
}
`
The last thing we have to do is pass data
from the query as a parameter to the blog page. Let’s console log it to see if it’s working.
// blog.js
...
const Blog = ({ data }) => (
<Layout>
<h1>Blog</h1>
{ console.log(data) }
</Layout>
)
Now let’s iterate over our blog data and create nodes for each of them.
Create two new files in the component folder called Post.js
and post.css
.
Post
will take five arguments:
title
author
description
date
path
// Post.js import React from "react" import { Link } from "gatsby" import "./post.css"
const Post = ({ title, author, date, description, path }) => ( <div className="post"> <h3 className="post-title">{title}</h3> <p className="post-description">{description}</p> <p className="post-written-by"> Written by {author} on {date} </p> <Link to={path}>Read more</Link> </div> )
export default Post
Here is the styling for our blog posts:
/* post.css */ .post { margin-bottom: 80px; padding-bottom: 80px; border-bottom: 2px solid white; }
.post .post-title { font-size: 3em; margin: 0; }
.post .post-description { font-size: 1.5em; }
.post .post-written-by { font-size: 1em; }
.post a { background-color: white; color: #a64ac9; padding: 16px 24px; text-decoration: none; margin-top: 16px; display: inline-block; }
Now we can import Post
into blog.js
and render a new post for each markdown file:
// blog.js
...
import Post from "../components/Post"
...
const Blog = ({ data }) => (
<Layout>
<h1>Blog</h1>
{data.allMarkdownRemark.edges.map(post => {
const { title, author, date, description, path } = post.node.frontmatter
<span class="token keyword">return</span> <span class="token punctuation">(</span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>Post</span>
<span class="token attr-name">title</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>title<span class="token punctuation">}</span></span>
<span class="token attr-name">author</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>author<span class="token punctuation">}</span></span>
<span class="token attr-name">date</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>date<span class="token punctuation">}</span></span>
<span class="token attr-name">description</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>description<span class="token punctuation">}</span></span>
<span class="token attr-name">key</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span><span class="token template-string"><span class="token string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>date<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">__</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>title<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">`</span></span><span class="token punctuation">}</span></span>
<span class="token attr-name">path</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>path<span class="token punctuation">}</span></span>
<span class="token punctuation">/></span></span>
<span class="token punctuation">)</span>
})}
</Layout>
)
Your blog should now look like this:
Now that we’ve generated a list of blogs on our blog page, how can we create a page for each blog which appears when the user clicks ‘Read More’?
We could manually create a page for each post, but this would be tedious.
Luckily Gatsby, in combination with node.js, provides functionality for dynamically generating pages.
Let’s first create the GraphQL query for retrieving data for an individual blog post.
When we created the GraphQL schema for retrieving all blog posts, we used the allMarkdownRemark
plugin.
This time, we only want the data for an individual blog post, so we’ll use the markdownRemark
plugin.
In the Explorer panel on the left, select:
`markdownRemark > frontmatter(purple) > eq: "_"
Be sure to select the purple frontmatter
for this part of the query; it’s an argument versus a field name.
This tells GraphQL that we will select a specific asset by their path, which will be passed as a parameter.
For this individual post we want to get several pieces of frontmatter data, so we’ll select
markdownRemark > html, frontmatter (blue) > author date path title
Be sure to select the blue frontmatter
for this part of the query; it’s a field name versus an argument.
Next, where we’re passing an argument to markdownRemark
, we have to give GraphQL an eq
value to look for. In our case, we’ll pass the path
for the blog post whose content we want to display.
We first need to pass this argument to our query, before we can pass it to the markdownRemark
plugin. You can also change the name of the query to be more semantic:
query BlogPost($path: String!) {
markdownRemark(frontmatter: { path: eq: $path }}) {
frontmatter {
author
date
title
path
}
html
}
}
String!
tells GraphQL that the path argument we’re passing is of type String
and is required.
Now let’s test if this query actually works.
If we press play, we get an error:
This is due to the fact that our query expects an argument but we haven’t passed it one!
Open the panel at the bottom called Query Variables and enter the following:
{
"path": "/april-fools"
}
When we press the play button now we get the data back for our April Fools blog.
Now that we have our query, what do we do with it?
First, let’s build a template which will denote how each blog post should be structured.
In the src/
directory, create a new folder called templates
and add a file inside called blogTemplate.js
.
We first need to include some imports:
// templates/blogTemplate.js
import React from "react"
import { graphql, Link } from "gatsby"
import Layout from "../components/Layout"
Next, let’s create the function skeleton:
// templates/blogTemplate.js
...
export default function Template({ data }) {
return ()
}
This template will take in our blog data and render it accordingly.
Finally, let’s add our GraphQL query to the bottom of the file.
// templates/blogTemplate.js
...
export const postQuery = graphql`
query BlogPost($path: String!) {
markdownRemark(frontmatter: { path: { eq: $path }}) {
frontmatter {
author
date
title
path
}
html
}
}
`
Now let’s construct our blog post structure. First, let’s grab the post
and the title
, author
, and date
from the data.
// templates/blogTemplate.js
...
export default function Template({ data }) {
const post = data.markdownRemark;
const { title, author, date } = post.frontmatter;
We’ll wrap our JSX in the <Layout>
component, and inside we’ll have:
- A link back to the blogs page
- The blog post title
- The posted by with author name and date
- A
<div>
containing the dangerouslySetInnerHTML
attribute, which takes the post.html
markup as the value.
// templates/blogTemplate.js
...
export default function Template({ data }) {
const post = data.markdownRemark;
const { title, author, date } = post.frontmatter;
return (
<Layout>
<Link to="/">Back to blogs</Link>
<h1>{title}</h1>
<p>Posted by {author} on {date}</p>
<div dangerouslySetInnerHTML={{ __html: post.html }} />
</Layout>
)
}
...
Here is the completed blogTemplate.js
file:
// templates/blogTemplate.js
import React from "react"
import { graphql, Link } from "gatsby"
import Layout from "../components/Layout"
export default function Template({ data }) {
const post = data.markdownRemark;
const { title, author, date } = post.frontmatter;
return (
<Layout>
<Link to="/">Back to blogs</Link>
<h1>{title}</h1>
<p>Posted by {author} on {date}</p>
<div dangerouslySetInnerHTML={{ __html: post.html }} />
</Layout>
)
}
export const postQuery = graphqlquery BlogPost($path: String!) { markdownRemark(frontmatter: { path: { eq: $path }}) { frontmatter { author date title path } html } }
Now that we have our template, let’s use it! We have to tell Gatsby to dynamically generate pages for each blog post, so let’s head over to gatsby-node.js
.
Let’s first require the path
module:
// gatsby-node.js
const path = require('path')
We’re going to use the exports.createPages
API to dynamically generate our pages.
// gatsby-node.js
...
exports.createPages = ({ boundActionCreators, graphql }) => {
const { createPages } = boundActionCreators
const postTemplate = path.resolve('src/templates/blogTemplate.js')
}
We now have to return a query to get all blog posts, so we can iterate over an generate our pages. We already have this query from a previous step, and all we need for each post is its path
.
...
return graphql(`
{
allMarkdownRemark {
edges {
node {
frontmatter {
path
}
}
}
}
}
`)
Once we receive a response back from the query, we want to reject the promise if an error occurred, and otherwise create a page for each post.
This will create a post at the designated path received from the query results, and will use the postTemplate
we declared above (our blogPost.js
template) to render each post.
...
return graphql(`
{
allMarkdownRemark {
edges {
node {
frontmatter {
path
}
}
}
}
}
`).then(res => {
if (res.errors) { return Promise.reject(res.errors) }
res<span class="token punctuation">.</span>data<span class="token punctuation">.</span>allMarkdownRemark<span class="token punctuation">.</span>edges<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">{</span> node <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token function">createPage</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
path<span class="token punctuation">:</span> node<span class="token punctuation">.</span>frontmatter<span class="token punctuation">.</span>path<span class="token punctuation">,</span>
component<span class="token punctuation">:</span> postTemplate
<span class="token punctuation">}</span><span class="token punctuation">)</span>
})
Here is the completed gatsby-node.js
file:
const path = require('path')
exports.createPages = ({ boundActionCreators, graphql }) => {
const { createPages } = boundActionCreators
const postTemplate = path.resolve('src/templates/blogTemplate.js')
}
return graphql({ allMarkdownRemark { edges { node { frontmatter { path } } } } }
).then(res => {
if (res.errors) { return Promise.reject(res.errors) }
res<span class="token punctuation">.</span>data<span class="token punctuation">.</span>allMarkdownRemark<span class="token punctuation">.</span>edges<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">{</span> node <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token function">createPage</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
path<span class="token punctuation">:</span> node<span class="token punctuation">.</span>frontmatter<span class="token punctuation">.</span>path<span class="token punctuation">,</span>
component<span class="token punctuation">:</span> postTemplate
<span class="token punctuation">}</span><span class="token punctuation">)</span>
})
Now we’re ready to see if it worked!
Re-start your development server, then head over to the browser and click one of the blog post “Read more” links:
I’m going to add a bit more styling to make it look nicer.
I’ll create a blogTemplate.css
file in the templates/
directory:
/* blogTemplate.css */
.blogTemplate .blogTemplate-title {
margin: 80px 0 24px;
}
.blogTemplate .blogTemplate-posted-by {
font-size: 1.2em;
}
.blogTemplate a {
color: #fff;
}
Then I’ll import the CSS file into blogTemplate.js
and add the appropriate class names. I’ll also wrap the JSX inside of <Layout>
in a <div>
so we can give it a class name of blogTemplate
.
// templates/blogTemplate.js
import React from "react"
import { graphql, Link } from "gatsby"
import Layout from "../components/Layout"
import "./blogTemplate.css"
export default function Template({ data }) {
const post = data.markdownRemark;
const { title, author, date } = post.frontmatter;
return (
<Layout>
<div className='blogTemplate'>
<Link to="/">Back to blogs</Link>
<h1 className="blogTemplate-title>{title}</h1>
<p className='blogTemplate-posted-by'>Posted by {author} on {date}</p>
<div dangerouslySetInnerHTML={{ __html: post.html }} />
</div>
</Layout>
)
}
export const postQuery = graphqlquery BlogPost($path: String!) { markdownRemark(frontmatter: { path: { eq: $path }}) { frontmatter { author date title path } html } }
Your blog post should look like this:
Now that we have a working blog, let’s deploy it to Netlify!
First, we need to establish our blog as a Git repository.
On GitHub, create a new repo:
With the terminal, in the project directory run the following commands:
git init
git add .
git commit -m "Adding my blog files"
git remote add origin <<repo-link>>
git push -u origin master
Once your code is on GitHub, it’s time to deploy!
Create an account on Netlify or sign in.
Click “New site from Git” and authenticate with GitHub.
Select your newly-created repository and click “Deploy.”
Every time you push to the master branch, your site will auto-deploy (you can change the deploy configuration but this is the default.)
You can even add a custom domain to really make the blog your own.
And that’s it! I hope this was helpful to explain the process for building a blog with Gatsby.
This process is tedious, so once you understand the architecture I would recommend using the Gatsby blog starter.
Feel free to contact me on Twitter if you have any questions.
Happy blogging!
gatsby-blog-tutorial's People
Recommend Projects
-
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
-
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
-