You have a Jamstack site. You're using Gatsby as your static site generator and Contentful to manage your content. You like Tailwind because its community offers a lot of solid visual components. You'd like to expose some of those components in your site so your team can just grab them and add them to pages in your site.

In this guide, we're going to see how to add a Tailwind component to your site on Stackbit.

Step 1: Create a new content type for the component on Contentful

Pick a beautiful component from a library like Tailwind UI. For this guide, we chose Tailwind UI's first "features" component.

Tailwind UI component screenshot

Whenever a component seems to include elements that could be repeated with code, like this example's four features, be sure to give those elements their own Contentful content types, linking to them from your main content type as reference fields set to "many references."

We'll work our way up from modeling a single child "feature item" to modeling the parent "features list" that represents our whole component.

Architectural diagram

Need a site to follow along? Try this Gatsby + Contentful + Tailwind starter. You can create it in Stackbit to start right away.

Under the Content model tab in Contentful, click Add content type.

Choose an appropriate name like "Feature Item" and click Create.

In a given small repeating feature item, if you see text, a hyperlink, or an image, create a Contentful field. In our case, that means planning for our content type to have two short text fields and one image field, which will replace Tailwind UI's sample hard-coded values of:

  1. "Competitive exchange rates", "No hidden fees", etc. → Title
  2. Various "Lorem ipsum..." → Subtitle
  3. Various purple SVG graphics → Image

Save your work.

Screenshot of a Contentful content type with 3 fields

Return to the Content model tab in Contentful, and Add content type again.

Choose an appropriate name like "Features List" and click Create.

At this higher level of data modeling, we'll translate text from the Tailwind UI's sample HTML to three fields. We'll also create one array:

  1. "Transactions" → Tagline
  2. "A better way to send money" → Title
  3. "Lorem ipsum ... quisquam" → Subtitle
  4. A reference field set to "many references" and restricted to "Feature Item" content → Features.

Screenshot of a multi-reference array in Contentful

Screenshot of a Contentful content type with 4 fields

The starter theme doesn't include a page type with a list of interchangeable sections, so we're all done data modeling in Contentful. If it did, we'd also want to edit its Contentful page data type to make "Features List" a valid data type for the field containing the list of sections.

☝️ If you'd like to add a sectioned page builder to your theme, visit our tutorials on making a landing page builder and sectioning an existing theme.

Wrapping up step 1, under the Content tab in Contentful, click Add entry and choose your new content type from the dropdown menu.

Create at least one piece of content in Contentful for each of your new content types.

Feel free to publish them, if you'd like. Or, to see draft content before publishing it, make sure your Gatsby theme is configured to use Contentful's preview API by configuring the gatsby-source-contentful plugin with the host: "preview.contentful.com" option. Use Contentful's Preview API key instead of the Delivery API Key to fetch unpublished data.

Screenshot of creating "Feature Item" content in Contentful

Screenshot of creating "Features List" content in Contentful

If you're working with a sectioned page builder theme, add your new content, such as a "Features List" record, to the sections list of an existing page on your site but don't publish the page just yet. Wait until you've put the accompanying Gatsby code into production.

Step 2: Add the new component to Gatsby

Prerequisite: install Tailwind CSS

Before editing your site's theme, be sure to install Tailwind CSS for Gatsby into the "preview" branch of your site (the one Stackbit's previews are rendering).

To validate your installation, use Stackbit's code editor to add the following HTML somewhere in your home page:

<p
  class="
    inline-flex items-center h-12 w-max px-6
    font-semibold text-green-100 bg-pink-900
    rounded-lg border-purple-400 border-4 border-dashed
"
>
  Hello world
</p>

If "Hello World" appears surrounded by a purple background and dashed border in Stackbit's preview within a few seconds, you're good to go.

Hello World button screenshot

  • Don't see it? Try restarting Stackbit's preview engine and/or reloading the browser page.
  • Updating, but slowly? Try Tailwind's just-in-time mode for faster edits.

Create new Gatsby components

Using the Stackbit code editor, create a new Gatsby template file to render your component -- or maybe more, if your component inherently involves nesting of repeated sub-components.

We're calling the file that will render the overall component src/components/FeaturesList.jsx (plural), and the file that will render each feature within it src/components/FeatureItem.jsx (singular).

Screenshot of adding a file to a folder in the Stackbit code editor

If you prefer, you can edit the preview branch of the GitHub repository Stackbit is managing for you through your favorite development workflow.

Implementation details will be particular to your site, but if the component you downloaded looks like this:

<div class="hello world">Sample text standing in for a title</div>

...then the JSX inside your exported default return() should look something like this:

<div className="hello world">{title}</div>

If a component includes elements that can be repeated with code, like the features in our component, break those elements out into their own React components. Iterating with the .map() function is a great way to pass appropriate parameters to your subcomponents.

Tailwind UI offers React samples for components, so this work may be mostly done for you, depending on the Tailwind component library you're using.

Make sure each new template file also exports a GraphQL fragment to tell the page rendering it what fields you created on your content type in Contentful. (Here's a great article about colocating fragments with the code they support, a position also endorsed by Apollo at GraphQL.com.)

Here's what our FeatureItem.jsx file looks like:

// src/components/FeatureItem.jsx

// JSX simplified for readability and to respect Tailwind UI licensing
import { graphql } from 'gatsby';
import PropTypes from 'prop-types';
import React from 'react';

const FeatureItem = (props) => {
  const { title, subtitle, image } = props;
  return (
    <div className="relative">
      <dt>
        <div className="absolute h-6 w-6 text-blue-600" dangerouslySetInnerHTML={{ __html: image.svg.content }} />
        <p className="ml-8 text-xl text-black">{title}</p>
      </dt>
      <dd className="ml-8 text-gray-700">{subtitle}</dd>
    </div>
  );
};

FeatureItem.propTypes = {
  title: PropTypes.string.isRequired,
  subtitle: PropTypes.string.isRequired,
  image: PropTypes.object.isRequired
};

export default FeatureItem;

export const query = graphql`
  fragment FeatureItemFragment on ContentfulFeatureItem {
    id
    title
    subtitle
    image {
      svg {
        content
      }
    }
  }
`;

(Note: you need to install the gatsby-transformer-inline-svg Gatsby plugin for image.svg.content to be available to your GraphQL fragment.)

And here's what our our FeaturesList.jsx file looks like -- note how it uses .map() to repeatedly call <FeatureItem />:

// src/components/FeaturesList.jsx

// JSX simplified for readability and to respect Tailwind UI licensing
import { graphql } from 'gatsby';
import PropTypes from 'prop-types';
import React from 'react';
import FeatureItem from './FeatureItem';

const FeaturesList = (props) => {
  const { tagline, title, subtitle, features } = props;
  return (
    <div className="px-4">
      <div>
        <h2 className="text-blue-600 uppercase">{tagline}</h2>
        <p className="text-4xl font-extrabold">{title}</p>
        <p className="text-gray-700">{subtitle}</p>
      </div>

      <dl>
        {features.map((featureItem) => (
          <FeatureItem {...featureItem} />
        ))}
      </dl>
    </div>
  );
};

FeaturesList.propTypes = {
  tagline: PropTypes.string.isRequired,
  title: PropTypes.string.isRequired,
  subtitle: PropTypes.string.isRequired
};

export default FeaturesList;

export const query = graphql`
  fragment FeaturesListFragment on ContentfulFeaturesList {
    id
    tagline
    title
    subtitle
    features {
      ...FeatureItemFragment
    }
  }
`;

Sometimes, Gatsby can't quite keep up with your changes, even if your code is perfect. If you run into problems that don't make sense (e.g. Gatsby not recognizing a Contentful GraphQL endpoint that definitely exists), try refreshing your browser page. If that doesn't help, restart the Stackbit preview engine:

  1. Click the settings gear icon in the upper left corner
  2. Click its Advanced tab at the upper left of the pop-up
  3. Click Restart in the lower right corner of the popup

Screenshot of the Stackbit restart preview button

Summon the new components from your theme

Now we need to summon Features.jsx from an existing page template, passing it a piece of "Features" content from Contentful.

For this example, we'll simply embed a reference to Feature directly into src/pages/index.js.

  • We need to import our new component into index.js:

    import Features from '../components/Features';
    
  • Right below <Hero />, we'll pass this piece of content to our new <Features /> component:

    <FeaturesList {...data.singlefeatureslist} />
    
  • Inside the definition of query, we'll edit the HomeQuery GraphQL query by adding a definition for a singlefeatureslist property right below the existing portfolio property. The new code fetches the first result from Gatsby's query against Contentful's "Features" content, ignoring any other query results that may exist:

      singlefeatureslist: contentfulFeaturesList {
        ...FeaturesListFragment
      }
    

Here is the new & improved index.js:

// src/pages/index.js

import { graphql } from 'gatsby';
import React from 'react';
import Cards from '../components/Cards';
import FeaturesList from '../components/FeaturesList';
import Hero from '../components/Hero';
import Layout from '../layouts/Layout';
import Newsletter from '../components/Newsletter';
import SiteMetadata from '../components/SiteMetadata';

const IndexPage = ({ data }) => {
  return (
    <Layout>
      <SiteMetadata title="Home" description="Portfolio of John Doe" />

      <Hero />

      <FeaturesList {...data.singlefeatureslist} />

      <div className="bg-gray-100 py-12 lg:py-16">
        {data.portfolio && data.portfolio.nodes.length > 0 ? (
          <Cards items={data.portfolio.nodes} />
        ) : (
          <div className="container">No projects found.</div>
        )}
      </div>
      <Newsletter />
    </Layout>
  );
};

export default IndexPage;

export const query = graphql`
  query HomeQuery {
    portfolio: allContentfulPortfolio {
      nodes {
        ...PortfolioCard
      }
    }
    singlefeatureslist: contentfulFeaturesList {
      ...FeaturesListFragment
    }
  }
`;

At this point, your new component should show up in your site preview in Stackbit. 🎊

Stackbit showing the new component

If you don't see your new component, try restarting Stackbit's preview engine and/or reloading the browser page.

Step 3: Profit: edit your site in Stackbit

If you haven't already, make the page editable by clicking the Content tab at the top center, to ensure you leave the Code tab you've been working with.

Click Add content from the side navbar -- or hover over a nearby one and click Add -- to add another feature to your Tailwind component.

Screenshot of editing component content in Stackbit

If you'd like, take a look at Contentful -- your changes from Stackbit will show up instantly.

Looking great? Publish your changes using Stackbit. Your edits to Gatsby and your data in Contentful should all go live. 🎉

Helpful resources