How To Build A JAMstack Website With TakeShape

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.

A screenshot of the JAMstack site you'll be building.

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 the homepageroute, which specifies the routing path (the index path, in this case) and what template file the route should use. Templates are all relative to the templatePath  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!