How To Build A JAMstack Website With TakeShape
In part 2 of this 4 part series, you'll learn how to use a headless cms to build a static site
This is part 2 of the 4 part series. Here's a demo of what you'll be building. Check out part 1 to get an introduction to TakeShape and learn content modeling and content creation. In part 3 you’ll learn how to deploy your static site to Netlify. In part 4 you’ll extend your skills even further and learn how to use TakeShape with Gatsby.js.
In part 1 you learned how to model and create content and making it available through TakeShape’s GraphQL API. Now you’ll learn how to use TakeShape’s static site generator in combination with that API to quickly create a JAMstack website. You’ll create and configure a TakeShape static site on your local machine, write GraphQL query files and use data returned from those queries in HTML templates.
If you want to skip setting up your static site from scratch you can grab the “Shape Portfolio” sample template as a starting point. Simply run the command git clone https://github.com/takeshape/takeshape-samples.git && cd takeshape-samples/shape-portfolio
. This will download a static site repo that’s pre-configured to work with the “Shape Portfolio” template in TakeShape. This repo has a more complex, styled, and scripted version of what we just built together. It might give you some inspiration and guidance on where to take your portfolio next.
Glad you decided to stay! We’ll be building that same shape-portfolio
static site from scratch so you can learn how it works. Ready, set, go!
Hello Portfolio
You’re going to start by cloning the blank-project
template from github, this will provide you with a bare-bones TakeShape setup. First, clone the repository to your computer, then copy the blank-project out into a new folder, and finally remove the cloned repo since you don’t need anything else from it. In your terminal, it looks like this:
> git clone https://github.com/takeshape/takeshape-samples.git
> cp -r takeshape-samples/blank-project/ shape-portfolio
> rm -rf takeshape-samples
Then you’ll enter the newly copied project folder, install the required software needed to run the project with npm, and initialize it with the TakeShape configuration:
> cd shape-portfolio
> npm install
> npm run init
Running init
will establish the connection between your local development environment and the TakeShape service. You’ll be prompted to make several sections to configure your environment. The first prompt will be for your TakeShape credentials. Input your TakeShape account email and password, then use the arrow keys to select your portfolio project. You’ll get a reminder about not having any sites set up yet (don’t worry, we’ll get to that in Part 3).
The initialization creates two files, .tsgrc
and graphql.config.json
. These are already in your project’s .gitignore
file because you started with the blank-project template.
You can now test that everything’s been set up correctly by running npm start
. If everything goes as expected, your default browser will open to localhost:5000
and display the blank-template page. Hooray! You’ve just run TakeShape from your local machine. Now it’s time to start hacking on your portfolio 🤓. If you get an error or aren’t able to load the site read any error messages that are output to the terminal to debug the problem.
Project Structure
By cloning the blank-project
template, you’ve been provided with some of the basic files and folders that TakeShape expects. We’ll explain a bit more about each of them now.
Configuration
At the root of your project is a file called tsg.yml
. It holds the configuration for your project and it’s the best place to start. Here’s what’s in tsg.yml
:
templatePath: src/templates
staticPath: static
buildPath: build
routes:
homepage:
path: /
template: pages/index.html
- At the top is the
templatePath
variable, which tells TakeShape where to look for template files when building your project. - Next is the
staticPath
variable, which tells TakeShape where to look for files like stylesheets, scripts, and static images like favicons. - After that is the
buildPath
variable, which is where TakeShape will place your project after it’s been built. - Finally, there’s the
routes
variable. Each child of this variables configures the routing rules of your project. Right now we only have thehomepage
route, which specifies the routing path (the index path, in this case) and what template file the route should use. Templates are all relative to thetemplatePath
set at the top of the file.
There are more complex ways to build routes, including dynamic variables that comes from the content you’ve created in TakeShape. We’ll get to this in a moment. First, let’s take a look at the templates you’re going to use.
Template Files
By default, TakeShape uses the Nunjucks templating system.
The src/templates
folder contains three starter subdirectories: data
, layouts
, pages
. data
contains GraphQL query files that retrieve content from the TakeShape API. pages
contains individual unique page templates. layouts
contains a base template that other templates extend. Inside layouts
is a starter layout template called default.html
, which looks like this:
<!doctype html>
<html>
<head>
<title>Blank Project - TakeShape</title>
<meta charset="utf-8">
<link rel="stylesheet" href="/stylesheets/main.css"/>
<link rel="author" href="humans.txt"/>
</head>
<body>
<div class="main">
{% block content %}{% endblock %}
</div>
<script src="/javascripts/main.js"></script>
</body>
</html>
As you can see, this provides the basis for other pages to wrap themselves in the layout by using Nunjuck’s extend functionality. Extending is accomplished by using the using {% extends "layouts/default.html" %}
and {% block %}
tags.
In pages/index.html
you can see how this template extends the default layout:
{% extends "layouts/default.html" %}
{% block content %}
<h1>Blank Project Template For TakeShape</h1>
{% endblock %}
With these basic foundations, you can build plenty of pages with minimal repetition.
Static Files
Finally, we’ll take a quick look at static files like stylesheets and scripts. The starting default.html
template above already links to the files /stylesheets/main.css
and /javascripts/main.js
. You’ll find these files in the top-level static
directory.
Building your portfolio
Homepage
Now that you know how everything fits together, you can start customizing your homepage to create an amazing portfolio!
First, you’ll want to display your projects on your homepage. To do this, you’ll need to add some data to the homepage route, write a query, and then render the data on the homepage. To add data to the route, you’ll update you homepage route configuration in tsg.yml
to look like this:
routes:
homepage:
path: /
template: pages/index.html
context: data/projects.graphql
Then create a projects.graphql
file in the src/templates/data
directory. In this file, you’re going to craft the query for your project data that’s sent to the TakeShape API. As long as you set up your Project content type following the pattern in Part 1, your file should look like this:
query {
projects: getProjectList {
items {
name
coverImage {
path
}
}
}
}
Now, you can use projects
as a variable in our homepage template, in order to render a list of all projects on the homepage. In pages/index.html
, you’ll update it to look like this:
{% extends "layouts/default.html" %}
{% block content %}
<ul>
{% for project in projects.items %}
<li>
<img src="{{ project.coverImage.path|image({h: 200, w: 300, fit: 'crop'}) }}">
<p><strong>{{ project.name }}</strong></p>
</li>
{% endfor %}
</ul>
{% endblock %}
For the cover image, note the image
filter being applied to the image’s path. TakeShape users get to host their images on imgix, an amazing and powerful image CDN, for free! The image
filter is a special TakeShape filter that accepts IMGIX filter parameters and returns the imgix URL for the image. We’ve set a custom height and width on the image and we’ve told imgix to crop the image to fit these dimensions. All the potential options are listed on imgix’s comprehensive API documentation.
Standalone project pages
Next, you’ll want to create standalone pages for your projects so that you can provide more detail about each one and link to projects directly.
To get started, update your tsg.yml
configuration with a new route for project pages:
project:
path: /projects/:name/
template: pages/project.html
paginate:
data: data/projects.graphql
itemName: project
This is a little more complex than our last route, so let’s break it down:
* The path
uses the variable :name
in order to render a unique URL for each project page. This variable comes right from the query we’re using in the `paginate` variable.
* paginate
tells TakeShape how to split up the data from the projects.graphql
query we created earlier into individual pages. In this case, each `item` in the query will get its own page. itemName
sets the variable that refers to each paginated item. In this case, we’ll access data from a variable named project
.
Next, you’ll want to add a few more fields to the projects.graphql
query so that you can present more information about each project on its standalone page. Update your query to look like this:
query {
projects: getProjectList {
items {
name
startDate
endDate
coverImage {
path
}
descriptionHtml
client {
name
url
}
}
}
}
Pretty straightforward! Think you’re getting the hang of building a site in TakeShape? Remember, you can explore the API for your entire project in TakeShape’s web interface. It’s really handy when writing queries yourself.
Now, you can create the pages/project.html
template to render out the project data into HTML. Here’s what that will look like:
{% extends "layouts/default.html" %}
{% block content %}
<article class="project">
<header>
<img class="project__cover-image" src="{{ project.coverImage.path|image({h: 600, w: 1200, fit: 'crop'}) }}">
<h1>{{ project.name }}</h1>
<div class="project__metadata">
<p>{{ project.startDate|date('YYYY') }}{% if project.endDate %} – {{ project.endDate|date('YYYY') }}{% endif %}</p>
{% if project.client %}
<p><a href="{{ project.client.url }}">{{ project.client.name }}</a></p>
{% endif %}
</div>
</header>
<div class="project__description">{{ project.descriptionHtml|safe }}</div>
</article>
{% endblock %}
This is pretty straightforward, too! Notice how the built-in TakeShape `date` filter is being used to format the start and end dates of your project so only the year is displayed. Also, notice that the safe
filter must be applied to the project description. This is because it’s output from the TakeShape API as raw HTML.
Now that each of your projects has a page, you want to be able to link to individual projects from your homepage. On the homepage template, wrap each project list item with an anchor tag <a href="{{project|route('project')}}">
. It should look like this:
{% extends "layouts/default.html" %}
{% block content %}
<ul>
{% for project in projects.items %}
<li>
<a href="{{ project|route('project') }}" title="{{ project.name }}">
<img src="{{ project.coverImage.path|image({h: 200, w: 300, fit: 'crop'}) }}">
<p><strong>{{ project.name }}</strong></p>
</a>
</li>
{% endfor %}
</ul>
{% endblock %}
route
is another special TakeShape filter. If you have a route defined for an object, like the one that defined a dynamic path for the project pages, then the route
filter will return the route for that item. It’s pretty handy! Just refer to the name of the route as it appears in your tsg.yml
and you’ll be good to go!
Now, if you run npm start
in your terminal, the website should build successfully. You will see a homepage with a list of your projects and you should be able to click through to each one.
For a next step, you can try creating an About page using your biography, or a page that lists all of your clients. Make sure to check out TakeShape’s documentation if you get stuck writing your configuration or templates.
Next Steps
In part 3 we’ll take the static site you just created and deploy it to Netlify! In just a few steps, you’ll be able to deploy your site to Netlify’s CDN and host your page on a secure HTTPS connection. How cool is that?
Until then, keep hacking!
Interested in JAMstack CMS?
JavaScript, APIs, and Markup, are the JAMStack. TakeShape is a headless GraphQL CMS and static site generator for building JAMstack websites. At TakeShape we're committed to building the best CMS tools possible for the most creative designers and developers. With our project templates, it's easy to get started. Plus, pricing is flexible and affordable. Sign up for a free account and spend more time being creative!