# gatsby-starter-mate: a portfolio starter that keeps its content out of the repository

> The interesting thing about this starter is that it is nearly empty. The pages, the components and the styling live in a separate theme package, and the content lives in a headless content service, so what a user forks is a configuration and a small amount of glue. The readme is unusually honest about the consequence: when you bootstrap the project, it does not contain many files, and that is the design rather than an omission.

**EmaSuriano/gatsby-starter-mate** — An accessible and fast portfolio starter for Gatsby integrated with Contentful CMS

- Repository: https://github.com/EmaSuriano/gatsby-starter-mate
- Website: https://gatsby-starter-mate.netlify.app
- Stars: 541 · Forks: 151
- Language: TypeScript
- License: 0BSD
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/emasuriano-gatsby-starter-mate

## The framing, and the two principles

The starter describes itself as an accessible and fast portfolio starter for Gatsby, integrated with a headless content service, and it names its audience in one line: developers and technical writers.

The reason it is worth reading is the sentence that comes next, which places the content service in the model role of a familiar pattern: the content service is the model when Gatsby with React is the view. The starter is entirely content based, so you write the content in the content service rather than in the repository, and the site is a rendering of that content.

And then the second principle, which is the one that explains why the repository is nearly empty. The components and the pages are decoupled inside a separate theme package. The readme gives the reason in the sentence the excerpt cuts off, but the shape of it is clear from the structure: a theme is a package that owns the page components and the styles and the data fetching, and the starter is the site that instantiates that theme with your own configuration and your own content.

That is a familiar and, in this case, sensible arrangement. A theme can be published and upgraded; a starter that contained the pages would be a fork you have to merge by hand. The trade is the one the readme names in the customisation section: when you first bootstrap the project, you notice that it does not contain many files, and the reason is that the structure separates where the data comes from from how it is displayed.

So the practical expectation to set is this. You are not getting a website. You are getting a configuration file, a content space, and a way to connect them, and everything you see on the demo site is the theme's doing. That is a better deal than it sounds, because a portfolio is mostly content and a little layout, and the content is the part that changes.

The licence is the zero-clause BSD one, which is about as permissive as a licence gets.

## The feature list, and the second data source

The feature list is a conventional starter's list, and it is worth reading for the two entries that are decisions rather than defaults.

The framework is the fifth generation of the site generator. The styling is a styled component system rather than a set of style sheets, which for a React site generator means the styles are components and the design tokens are a theme, and a palette change is a prop rather than a stylesheet edit. A reveal-on-scroll animation library is included, which is the kind of thing a portfolio wants and a corporate site does not, and it is the sort of choice that tells you the intended audience.

The content integration is the headless service, and the image and manifest plugins mean the site is installable as a progressive web application and that its images are processed at build time rather than shipped as originals. Search engine metadata, a responsive layout, an icon set, deployment support for two hosting products, social sharing buttons, and an analytics integration complete the list.

The entry that stands out is the second data source. The starter is described as extremely easy to extend with more than one source of data, and the demo comes with an integration that pulls posts from a blogging platform based on a user name.

That example is chosen carefully, because it demonstrates the point better than a blog integration would. A blogging platform has no content model of its own that you can design, so the integration is a matter of mapping somebody else's shape onto yours, or into a shape the theme knows how to render. The second source is the list of writing, and it comes from a platform you do not control, and the site still renders it alongside the content you do control.

Which is the real argument for a static site generator with a plugin ecosystem: the page is assembled from several sources, each with its own shape, and the generator resolves them into one page at build time. The alternative, a database query per page at request time, is a different architecture with different costs, and the starter makes the choice for you by being a static site in the first place.

So a user of this starter gets a personal site whose content lives in a service they can edit, whose writing comes from somewhere else, and whose layout comes from a theme they did not write. The three sources are decoupled, and the readme's mention of the content service being the model is the summary of the whole arrangement.

## The setup, and the error message that teaches it

The setup instructions are unusually good, and the reason is that the author knows the first run fails and has decided to make the failure useful.

There is a tutorial video and a written document, and the readme recommends the framework's own setup guide for people who have never used the generator before. There is also a step that is easy to miss and important: setting the preferred package manager inside the generator's own command line tool, so that the commands that follow use the same package manager the project uses.

```bash
$ gatsby options set pm yarn
info Preferred package manager set to "yarn"
```

Then the copy, which is the generator's own command with the destination directory and the source address.

```bash
$ gatsby new mate-portfolio https://github.com/EmaSuriano/gatsby-starter-mate
```

And then the failure. The readme tells you that at this point you have the repository with its dependencies installed, and that if you try to start it you will get a particular message.

```bash
  TypeError: Expected parameter accessToken
```

That is a well-chosen failure, and the readme explains it in one sentence: the portfolio does not know which content space to take the information from, because you have not told it. The error names the missing thing precisely, and the next step is to create an empty space in the content service.

Then the setup command, and this is the part that makes the starter a starter rather than a template.

```bash
yarn setup
```

It asks for three values: the space identifier, the delivery API access token, and a personal access token. All three are in the settings section of the service, under the API keys, and the readme says which is which.

The distinction between the two tokens is the security-relevant detail and it is easy to miss. The delivery token is the one the site uses to read content at build time, and it can be exposed to a build service. The personal token is the one that can write, and it is needed only for the setup step, because the setup copies content into your space.

And then it does the thing that makes this starter pleasant to try. The setup command copies all the content models and the content itself from a prepared demo space into yours, so your new space is not empty but populated with the same entries the demo site shows. The readme shows the terminal output, which is a table of what is about to be imported: three content types, three editor interfaces, eight entries, six assets, one locale and no webhooks, followed by a progress log of the import steps with their timings.

The editor interfaces are the part that shows the care. A content type without an editor interface is a set of fields with no form, which means editing the demo content in the service is a matter of filling in a form rather than guessing the field names. A starter that imports models but not interfaces would leave the user with a space they cannot comfortably edit, and this one does not.

Finally the development server, and the site is at a local address.

## Two routes, and what the single page application costs

The readme is explicit that the site is a single page application and that it therefore has only two routes. The main one carries four sections: the landing section, the about section, the projects section and the writing section. The second is the error page for an unexpected route.

That is a small design decision with a large consequence, and it is worth being clear about it, because a portfolio is exactly the kind of site where people expect separate pages.

The consequence is that the four sections are not four URLs. They are four sections of one document, and the site has no separate page for a project, so a visitor cannot share a link to one project, and a search engine sees one page rather than four. The error route is the only other thing the router knows about.

For a personal portfolio with a handful of sections that is a defensible trade. A single document is one request, it is trivially fast, and there is no navigation model to get wrong. The cost is that the site is worse at being a hub, and worse at being referenced at a fine grain, which for a developer whose projects are the point is not nothing.

The readme's own screenshots section makes the structure visible: a table with one row per section and the corresponding image, plus the error page. Five images for a whole site, and the images are named after the sections rather than after pages, which confirms the structure.

There is one place where the choice shows up in a way that matters, and that is the writing section. The writing comes from the blogging platform, and the individual posts are not routes in this site. So the list of posts is a section with links out, rather than a set of pages the site renders itself. Whether that is right depends on whether you want your writing hosted on the platform where it has an audience, or on your own domain where it is yours; the starter's answer is the first, and it is the answer that makes sense if the writing is the traffic rather than the portfolio.

The error page is the other half of the picture, and its existence is a good sign. A site that routes everything to one page still needs to handle the case where somebody follows an old link or types a bad address, and the starter ships a designed page for it.

## Building, deploying, and the two tokens

The build section is short and it is where the content service's two tokens come back, so it is worth reading carefully.

The environment file is excluded by the ignore file, which is the right thing to do, and the consequence is stated plainly: in order to deploy the site you have to send the space identifier and the access token with the build command.

```bash
SPACE_ID=xxxxx ACCESS_TOKEN=yyyyy yarn build
```

The result goes into the public folder, which the readme says you can upload to any web host, and then it recommends one in particular, because that one lets you define the build command and set the environment variables in the site's configuration rather than in a shell.

So the deployment story is: a static bundle in a folder, two environment variables, and a host that can run a build command. That is the standard modern static site deployment, and the reason the starter suggests one host over another is not a technical limitation but the convenience of the environment variable configuration.

The security point is the one worth drawing out, because it is easy to get wrong. The token in the build command is the delivery token, and the delivery token is a read-only credential for published content. It appears in the build environment, and on a hosted build service it is visible in the site's configuration to anyone with access to the project settings. That is acceptable for a read token whose only power is to fetch content that is, by definition, published.

The personal token is different, and it never appears in the build. It is used once, by the setup command, to copy the demo content into your space. The design keeps the write credential out of the build entirely, which means a leaked build configuration leaks only what was already public. That separation is not called out as a feature anywhere, but it is the reason the setup is a separate command rather than part of the build, and it is the right way round.

There is also a detail about the environment file being written. The setup command's output includes a line saying the configuration file was written and where, and the path in the example is a developer's own directory. So the setup writes the credentials it was given into a local file that the ignore file excludes, and the developer never has to copy a token by hand.

## The repository, and the manifest

The top level of the repository is short, and it is short by design.

There is a configuration file for the generator, written in TypeScript, and a file of the same name in the JavaScript flavour, which is the file where the site is wired to its theme and where the content sources are declared. There is a source directory, and a bin directory which holds the setup script, and a media directory with the screenshots, and an icon image. There is a lock file, a type configuration, a linter configuration, and a file pinning the node version the project expects.

That last one is worth a moment, because the starter's whole value proposition is that it is easy to start. A starter that pins the node version is a starter that will not break when the framework's own dependencies move, and the file doing the pinning is a single line that says which version.

The manifest tells the same story in the scripts. There is a build and a start, and both of them run the generator through a wrapper that loads environment variables, with a flag to silence the wrapper's own output. That wrapper is the mechanism by which the local development server and the build both get their credentials from the environment file that the setup command wrote, and the silent flag is there because the wrapper is chatty and its output would otherwise be interleaved with the generator's.

There is a separate script for serving the built site, one for cleaning the build output, one that runs the setup command through a TypeScript runner with an explicit override to compile the modules as CommonJS, one that type checks the project without emitting, and one that lints the source directory only. The last two are the interesting ones: a starter that offers a type check and a lint as separate commands is a starter that expects people to keep working in it, which is the opposite of a template that you fork and abandon.

The dependency list is the expected one for a site of this kind, and reading it tells you the stack. The styling is a preset on top of the styled component system with the emotion runtime, and there is a babel plugin for the styled component system's own syntax. The icons are the scalable icon library with its React bindings, and there is a separate plugin that wires that library into the generator's font loading. The generator plugins cover the manifest, the image processing, the sharp image library, the metadata through a helmet plugin, the analytics, and the TypeScript support. The data sources are the content service and the blogging platform, and there is a Markdown transformer for the bodies.

And the theme itself is not in the dependency list of the excerpt, because the list is cut off, but the readme's customisation section names the theme package explicitly, and it is the package that holds everything a user does not get in this repository.

## Conclusion

gatsby-starter-mate fits a developer or technical writer who wants a personal site with the content in a place they can edit without touching the code, and who wants the site to be a progressive web application with search engine metadata already in place. It does not fit somebody who wants a site whose content is versioned alongside its code. Verify first that you are comfortable with a build that needs two tokens rather than one, since the reading token is safe to expose and the writing one is not, that the content model you need matches the one the demo space defines, and that the theme you are relying on is versioned separately, because that is where the pages and the components actually live.

## FAQ

### What does the gatsby-starter-mate starter do?

It is a portfolio site for developers and technical writers whose content lives in a headless content service rather than in the repository, with the pages and components supplied by a separate theme package. The demo also pulls a list of posts from a blogging platform, so the site assembles its content from more than one source.

### Why does the starter have so few files?

Because it is built on two principles: the content comes from an external service, and the components and pages live in a decoupled theme package. The repository holds the configuration, the source, and the setup script; the pages, the components and the styling are in the theme, which can be published and upgraded rather than forked and merged by hand.

### What does the setup command of the starter do?

It asks for three values, the space identifier, the delivery API token and a personal access token, writes them to a local environment file, and then copies the content models, the editor interfaces and the content from a prepared demo space into your own space, so you start with a populated space rather than an empty one.

### How do I deploy a site built with this starter?

The build produces a static bundle in a public folder, and the build command needs the space identifier and the access token in the environment. The readme recommends a host where you can set the build command and the environment variables in the site configuration, and notes that the environment file itself is excluded from version control.

### Does the starter produce separate pages for each section?

No. The site is a single page application with two routes: the main one, carrying the landing, about, projects and writing sections, and the error page. The consequence is that the sections cannot be linked to individually and the whole site is one document for a search engine to index.

## Sources

- [EmaSuriano/gatsby-starter-mate on GitHub](https://github.com/EmaSuriano/gatsby-starter-mate)
- [Issues](https://github.com/EmaSuriano/gatsby-starter-mate/issues)
- [License: 0BSD](https://github.com/EmaSuriano/gatsby-starter-mate/blob/master/LICENSE)
- [Project website](https://gatsby-starter-mate.netlify.app)
- [README](https://github.com/EmaSuriano/gatsby-starter-mate/blob/master/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/emasuriano-gatsby-starter-mate
