Create Gatsby and Wordpress portfolio website step by step
In this article i will explain you step by step to create gatsby portfolio website using wordpress as headless cms
Step 1: Creating Wordpress Website
In order to do a local setup for the wordpress download local wp from this link Local Wp and you can create your wordpress website there
Step 2: Install necessary plugin required for running graphql query
In order to run graphql query and also for accessing data from gatsby website we need to install plugin so that we can access wordpress data as REST API from gatsby website. here are the list of plugins that we need to install and activate
-
Advance Custom Fields
-
WP Gatsby
-
WP Graphql
-
WPGraphQL for Advanced Custom Fields : this plugin you need to download externally and upload from this link WPGraphQL for Advanced Custom Fields
Step 3: Create and Install custom theme in wordpress
Here we are going to create our own theme and install it in wordpress, this theme will prevent anyone from accessing wordpress link so that wordpress act only as headless cms
-
In your wordpress local which you have installed previously to create wordpress site you will find an option to go to site folder just click that link and you will be automatically navigated to the files and folder for your wordpress site
-
In your wordpress website folder just navigate to app->public->wp-content->themes
-
Inside themes folder create a your theme folder with any name of your choice i'm going to name it as wp-gatsby-js-theme
-
Once you have created your theme folder just navigate to that folder and create following files
-
functions.php
-
index.php
-
style.css
-
portfolio_under_content.php : this file is custom template file for our custom post type
-
Any theme image of your choice just name it as screenshot.png
-
-
Once you have created the above file just copy paste following code
Leave the file style.css blank in index.php just add php open and close tag
Paste the following code in functions.php
<?php
add_theme_support( 'custom-logo' );
add_theme_support( 'menus' );
add_theme_support('post-thumbnails');
function add_nav_menus() {
register_nav_menus( array(
'nav menu'=>'Navigation Bar',
'footer menu'=> 'Footer Bar',
));
}
add_action('init', 'add_nav_menus');
function create_custom_portfolio_post_type(){
register_post_type('portfolio', array(
'labels'=>array(
'name'=>__('Portfolio'),
'singular_name'=>__('Portfolio')
),
'public'=> true,
'show_in_graphql' => true,
'graphql_single_name' => 'Portfolio',
'graphql_plural_name' => 'Portfolios',
'show_in_admin_bar'=> true,
'show_in_rest' => true
));
add_post_type_support('portfolio', array(
'thumbnail',
'excerpt'
));
}
add_action('init', 'create_custom_portfolio_post_type');
?>
Paste the following code in portfolio_under_content.php
<?php /* Template Name: Portfolio Items below content */ ?>
- Once you have added the respective code login to wordpress admin and activate the theme
Step 4: Add Post to your custom post type portfolio, add pages, add menus in wordpress
-
Once you have activated the theme above you will be able to see a menu with the name Portfolio just click on that then you will be able to add new portfolio with images and excerpt please add 1 or two portfolios with featured images and excerpt
-
Create page such as portfolio, home etc by navigating to pages -> add new
-
When you create portfolio page make sure to select template as Portfolio under content as custom template
-
Create Advance custom fields and make sure to select all the settings as show in screen shot below
- Create menu for your header by navigating to appearance -> menu link in sidebar, make sure all the settings are same as shown in screen shot below
Step 5: Create Gatsby portfolio website and install necessary plugins
Create a fresh gatsby website using following steps
- Install gatsby cli using following command
npm install --save gatsby-cli
- Create gatsby website using following command
gatsby new 'your-website-name'
- Navigate to the path of your newly created project and install the following package
npm install --save gatsby-source-graphql bluebird styled-components
- Go to your project folder and find gatsby-config.js and add this piece of code to your plugins array
{
resolve: `gatsby-source-graphql`,
options: {
typeName: "WPGraphQL",
fieldName: "wpcontent",
url: `http://gatsbywordpress.local/graphql`,
},
},
- Go to your project folder again and find gatsby-node.js and just copy and replace the whole code with the following code
const _ = require(`lodash`);
const Promise = require(`bluebird`);
const path = require(`path`);
const slash = require(`slash`);
// Implement the Gatsby API “createPages”. This is
// called after the Gatsby bootstrap is finished so you have
// access to any information necessary to programmatically
// create pages.
// Will create pages for WordPress pages (route : /{slug})
// Will create pages for WordPress posts (route : /post/{slug})
exports.createPages = ({ graphql, actions }) => {
const { createPage, createRedirect } = actions;
createRedirect({
fromPath: "/",
toPath: "/home",
redirectInBrowser: true,
IsPermanent: true,
});
return new Promise((resolve, reject) => {
// The “graphql” function allows us to run arbitrary
// queries against the local WordPress graphql schema. Think of
// it like the site has a built-in database constructed
// from the fetched data that you can run queries against.
// ==== PAGES (WORDPRESS NATIVE) ====
graphql(
`
{
wpcontent {
pages {
edges {
node {
id
slug
status
template {
templateName
}
title
content
}
}
}
}
}
`
)
.then((result) => {
if (result.errors) {
console.log(result.errors);
reject(result.errors);
}
// Create Page pages.
const pageTemplate = path.resolve("./src/templates/page.js");
const portfolioUnderContentTemplate = path.resolve(
"./src/templates/portfolioUnderContent.js"
);
// We want to create a detailed page for each
// page node. We'll just use the WordPress Slug for the slug.
// The Page ID is prefixed with 'PAGE_'
_.each(result.data.wpcontent.pages.edges, (edge) => {
// Gatsby uses Redux to manage its internal state.
// Plugins and sites can use functions like "createPage"
// to interact with Gatsby.
createPage({
// Each page is required to have a `path` as well
// as a template component. The `context` is
// optional but is often necessary so the template
// can query data specific to each page.
path: `/${edge.node.slug}/`,
component: slash(
edge.node.template &&
edge.node.template.templateName ===
"Portfolio Items Below Content"
? portfolioUnderContentTemplate
: pageTemplate
),
context: edge.node,
});
});
})
// ==== END PAGES ====
// ==== PORTFOLIO ====
.then(() => {
graphql(
`
{
wpcontent {
portfolios {
edges {
node {
content
portfolio {
portfolioUrl
}
title
slug
link
featuredImage {
node {
sourceUrl
}
}
excerpt
}
}
}
}
}
`
).then((result) => {
if (result.errors) {
console.log(result.errors);
reject(result.errors);
}
const portfolioTemplate = path.resolve(
"./src/templates/portfolio.js"
);
// We want to create a detailed page for each
// post node. We'll just use the WordPress Slug for the slug.
// The Post ID is prefixed with 'POST_'
_.each(result.data.wpcontent.portfolios.edges, (edge) => {
createPage({
path: `/portfolio/${edge.node.slug}/`,
component: slash(portfolioTemplate),
context: edge.node,
});
});
});
})
// ==== END PORTFOLIO ====
// ==== BLOG POSTS ====
.then(() => {
graphql(
`
{
wpcontent {
posts {
edges {
node {
excerpt
id
date
title
content
slug
}
}
}
}
}
`
).then((result) => {
if (result.errors) {
console.log(result.errors);
reject(result.errors);
}
const posts = result.data.wpcontent.posts.edges;
const postsPerPage = 2;
const numberOfPages = Math.ceil(posts.length / postsPerPage);
const blogPostListTemplate = path.resolve(
"./src/templates/blogPostList.js"
);
Array.from({ length: numberOfPages }).forEach((page, index) => {
createPage({
component: slash(blogPostListTemplate),
path: index === 0 ? "/blog" : `/blog/${index + 1}`,
context: {
posts: posts.slice(
index * postsPerPage,
index * postsPerPage + postsPerPage
),
numberOfPages,
currentPage: index + 1,
},
});
});
const pageTemplate = path.resolve("./src/templates/page.js");
_.each(posts, (post) => {
createPage({
path: `/post/${post.node.slug}`,
component: slash(pageTemplate),
context: post.node,
});
});
resolve();
});
});
// ==== END POSTS ====
});
};
-
Go to your project folder again and find templates folder or create a folder templates if it doesn't exist inside src folder and create following templates
-
Go to your project folder and Create template blogPostList.js under src->templates folder and copy paste the following code
import React from "react";
import Layout from "../components/layout";
import { Link } from "gatsby";
import styled from "styled-components";
import Seo from "../components/seo";
const Pagination = styled.div`
display: flex;
justify-content: flex-end;
`;
const PageNumberWrapper = styled.div`
border: 1px solid #eee;
background: ${(props) => (props.isCurrentPage ? "#eee" : "white")};
`;
const PageNumber = styled(Link)`
display: block;
padding: 8px 16px;
`;
const BlogList = ({ pageContext }) => (
<Layout>
<Seo title={"Blog"} />
{pageContext.posts.map((post) => (
<div key={post.node.wordpress_id}>
<h3 dangerouslySetInnerHTML={{ __html: post.node.title }} />
<small>{post.node.date}</small>
<p dangerouslySetInnerHTML={{ __html: post.node.excerpt }} />
<div>
<Link to={`/post/${post.node.slug}`}>Read more</Link>
</div>
</div>
))}
<Pagination>
{Array.from({ length: pageContext.numberOfPages }).map((page, index) => (
<PageNumberWrapper
key={index}
isCurrentPage={index + 1 === pageContext.currentPage}
>
<PageNumber to={index === 0 ? "/blog" : `/blog/${index + 1}`}>
{index + 1}
</PageNumber>
</PageNumberWrapper>
))}
</Pagination>
</Layout>
);
export default BlogList;
- Go to your project folder Create template page.js under src->templates folder and copy paste the following code
import React from "react";
import Layout from "../components/layout";
import Seo from "../components/seo";
//import PortfolioItems from "../components/PortfolioItems"
const Page = ({ pageContext }) => (
<Layout>
<Seo title={pageContext.title} />
<h1 dangerouslySetInnerHTML={{ __html: pageContext.title }} />
<div dangerouslySetInnerHTML={{ __html: pageContext.content }} />
</Layout>
);
export default Page;
- Go to your project folder Create template portfolio.js under src->templates folder and copy paste the following code
import React from "react";
import Layout from "../components/layout";
import styled from "styled-components";
import Seo from "../components/seo";
const FeaturedImage = styled.img`
max-width: 300px;
margin: 16px 0;
`;
const portfolio = ({ pageContext }) => (
<Layout>
<Seo title={pageContext.title} />
<h1>{pageContext.title}</h1>
<strong>Website url:</strong>
<a
href={pageContext.portfolio.portfolioUrl}
target="_blank"
rel="noopener noreferrer"
>
{pageContext.portfolio.portfolioUrl}
</a>
<div>
<FeaturedImage src={pageContext.featuredImage.node.sourceUrl} />
</div>
<div dangerouslySetInnerHTML={{ __html: pageContext.content }} />
</Layout>
);
export default portfolio;
- Go to your project folder Create template portfolioUnderContent.js under src->templates fodler and copy paste the following code
import React from "react";
import Layout from "../components/layout";
import PortfolioItems from "../components/PortfolioItems";
import Seo from "../components/seo";
const PorfolioUnderContent = ({ pageContext }) => (
<Layout>
<Seo title={pageContext.title} />
<h1 dangerouslySetInnerHTML={{ __html: pageContext.title }} />
<div dangerouslySetInnerHTML={{ __html: pageContext.content }} />
<PortfolioItems />
</Layout>
);
export default PorfolioUnderContent;
- Go to your project folder Create templates post.js under src->templates folder and copy paste the following code
import React from "react";
const BlogPost = ({ pageContext }) => {
return (
<div>
<h1>{pageContext.title}</h1>
</div>
);
};
export default BlogPost;
Step 5: Update the layout of your gatsby website
We need to add layout and other necessary component to make our site fully functional
- Updating layout Go to your project folder and finsd components folder and inside that folder create file layout.js and just copy paste the following code
import React from "react";
import MainMenu from "./MainMenu";
import styled, { createGlobalStyle } from "styled-components";
const GlobalStyles = createGlobalStyle`
@import url('https://fonts.googleapis.com/css?family=Open+Sans:300,300i,400,400i,600,600i,700,700i,800,800i');
body, html{
font-family: 'Open Sans', sans-serif;
margin: 0 !important;
padding: 0 !important;
}
`;
const LayoutWrapper = styled.div`
max-width: 960px;
margin: 0 auto;
`;
const Layout = ({ children }) => (
<div>
<GlobalStyles />
<MainMenu />
<LayoutWrapper>{children}</LayoutWrapper>
</div>
);
export default Layout;
- Updating menu Go to your project folder and finsd components folder and inside that folder create file MainMenu.js and just copy paste the following code
import React from "react";
import { graphql, StaticQuery, Link } from "gatsby";
import styled from "styled-components";
import SiteInfo from "./SiteInfo";
const MainMenuWrapper = styled.div`
display: flex;
background-color: #212529;
`;
const MainMenuInner = styled.div`
max-width: 960px;
margin: 0 auto;
display: flex;
width: 960px;
height: 100%;
`;
const MenuItem = styled(Link)`
color: white;
display: block;
padding: 16px 16px;
`;
const MainMenu = () => (
<StaticQuery
query={graphql`
{
wpcontent {
menus(where: { slug: "main-menu" }) {
nodes {
menuItems {
edges {
node {
url
description
label
path
}
}
}
}
}
}
}
`}
render={(props) => {
return (
<MainMenuWrapper>
<MainMenuInner>
<SiteInfo />
{props.wpcontent.menus.nodes[0].menuItems.edges.map((item) => (
<MenuItem to={`${item.node.path}`} key={item.node.label}>
{item.node.label}
</MenuItem>
))}
</MainMenuInner>
</MainMenuWrapper>
);
}}
/>
);
export default MainMenu;
- Updating portfolio items Go to your project folder and finsd components folder and inside that folder create file PortfolioItems.js and just copy paste the following code
import React from "react";
import { graphql, StaticQuery, Link } from "gatsby";
import styled from "styled-components";
const PortfolioItemsWrapper = styled.div`
display: flex;
justify-content: center;
`;
const PortfolioItem = styled.div`
width: 300px;
border: 1px solid #efefef;
padding: 16px;
margin: 16px;
`;
const PortfolioImage = styled.img`
max-width: 100%;
`;
const PortfolioItems = () => {
return (
<StaticQuery
query={graphql`
{
wpcontent {
portfolios {
edges {
node {
content
title
slug
link
featuredImage {
node {
sourceUrl
}
}
excerpt
}
}
}
}
}
`}
render={(props) => (
<PortfolioItemsWrapper>
{props.wpcontent.portfolios.edges.map((portfolioItem) => (
<PortfolioItem key={portfolioItem.node.id}>
<h2>{portfolioItem.node.title}</h2>
<PortfolioImage
src={portfolioItem.node.featuredImage.node.sourceUrl}
alt="Thumbnail"
/>
<div
dangerouslySetInnerHTML={{ __html: portfolioItem.node.excerpt }}
/>
<Link to={`/portfolio/${portfolioItem.node.slug}`}>
Read more
</Link>
</PortfolioItem>
))}
</PortfolioItemsWrapper>
)}
/>
);
};
export default PortfolioItems;
- Updating seo template used for seo purpose Go to your project folder and finsd components folder and inside that folder create file seo.js and just copy paste the following code
/**
* SEO component that queries for data with
* Gatsby's useStaticQuery React hook
*
* See: https://www.gatsbyjs.com/docs/use-static-query/
*/
import * as React from "react";
import PropTypes from "prop-types";
import { Helmet } from "react-helmet";
import { useStaticQuery, graphql } from "gatsby";
function Seo({ description, lang, meta, title }) {
const { site } = useStaticQuery(
graphql`
query {
site {
siteMetadata {
title
description
author
}
}
}
`
);
const metaDescription = description || site.siteMetadata.description;
const defaultTitle = site.siteMetadata?.title;
return (
<Helmet
htmlAttributes={{
lang,
}}
title={title}
titleTemplate={defaultTitle ? `%s / ${defaultTitle}` : null}
meta={[
{
name: `description`,
content: metaDescription,
},
{
property: `og:title`,
content: title,
},
{
property: `og:description`,
content: metaDescription,
},
{
property: `og:type`,
content: `website`,
},
{
name: `twitter:card`,
content: `summary`,
},
{
name: `twitter:creator`,
content: site.siteMetadata?.author || ``,
},
{
name: `twitter:title`,
content: title,
},
{
name: `twitter:description`,
content: metaDescription,
},
].concat(meta)}
>
<link
href="https://fonts.googleapis.com/css?family=Open+Sans"
rel="stylesheet"
/>
</Helmet>
);
}
Seo.defaultProps = {
lang: `en`,
meta: [],
description: ``,
};
Seo.propTypes = {
description: PropTypes.string,
lang: PropTypes.string,
meta: PropTypes.arrayOf(PropTypes.object),
title: PropTypes.string.isRequired,
};
export default Seo;
- Updating Site Info to be diaplyed in header Go to your project folder and find components folder and inside that folder create file SiteInfo.js and just copy paste the following code
import React from "react";
import { graphql, StaticQuery } from "gatsby";
import styled from "styled-components";
const SiteInfoWrapper = styled.div`
flex-grow: 1;
color: white;
margin: auto 0;
`;
const SiteTitle = styled.div`
font-weight: bold;
`;
const SiteInfo = () => (
<StaticQuery
query={graphql`
{
wpcontent {
generalSettings {
title
url
description
}
}
}
`}
render={(props) => (
<SiteInfoWrapper>
<SiteTitle>{props.wpcontent.generalSettings.title}</SiteTitle>
<div>{props.wpcontent.generalSettings.description}</div>
</SiteInfoWrapper>
)}
/>
);
export default SiteInfo;
Step 6: Run the gatsby website and wordpress website
Run the gatsby website using command
npm start
Visit your local url and congratulations just now you have created full fledged gatsby and wordpress website