Skip to content

My Personal Blog Site based on GatsbyJS ( SSG ) and DatoCMS

License

Notifications You must be signed in to change notification settings

argahv/portfolio

Repository files navigation

Netlify Status

Headless Multilingual Starter for Gatsby 4 with DatoCMS

Multilanguage starter for Gatsby completely driven by an headless CMS.

Live Demo


Lighthouse scores

Performance Accessibility Best Pratices SEO PWA
98 ~ 100 100 100 100 8/9

Run Lighthouse Test


Features

  • 100% Headless: create pages and articles, define languages, branding, blog settings and localize SEO, PWA settings, slugs and much more directly on DatoCMS.
  • Language switcher component swapping between different and equal slugs per locale
  • Automatic internal links localization using DAST and custom <Navigator /> component
  • Multiple per-locale PWA webmanifest files generation on build time, dynamically injected based on current language.
  • Browser locale detection and redirection
  • Supports any language code path such as "/en-GB" or "/en",
  • Correspondent localized content queried directly inside components by using GraphQL and useStaticQuery
  • Paginated archive pages, prev/next article navigation, social sharing and synthax highlighting.

CMS View

CMS View


Table of contents


Purpose of the package

The purpose is to provide a full JAMstack multilingual boilerplate which allows editors to add/remove languages, translate slugs, SEO, PWA, menus, pages, posts and much more directly on DatoCMS.

By enforcing fields validation and presentation, it is possible to safely manage those aspects of the app outside of Gatsby.

Since language switcher, localized meta tags injections, webmanifest generation, redirects and path generation are already set-up, you can truly focus on your content.

After publishing any change, a build on Gatsby Cloud will automatically be executed and the website will be updated with new localized content.


Content of the package

/src Content
/components/layout Layout and most common reusable components
/components/ui Specific UI components
/components/langHelpers <LanguageSwitcher />, <Navigator />, <Redirect /> and <Seo />
/components/vectors JSX icons
/context Language provider
/hooks Useful useStaticQuery hooks
/static colors.json generated during build
/templates JSX templates
/pages 404 page
/utils Useful functions

Why DatoCMS

As of writing, Gatsby Cloud integrates perfectly with four headless CMSs, Dato is by far my favourite one: it is simple, powerful and honest. The query schema is excellently crafted and makes possible to handle data in a very simple and clean way.

Moreover, you can enjoy instant previews on Gatsby Cloud, without any futher configuration, just save the draft, wait for 5 seconds and preview the content.

if you are not familiar with DatoCMS, the following links will be useful:

However, multi-language features of this starter can be adapted and used with other headless CMSs as long as locales and localized data are queriable in a Dato-like way.


Important notes

DatoCMS - The entire project content and many key settings are hosted on DatoCMS, this means that any content must be entered perfectly: create fieldsets, enforce fields validation and presentation, check carefully field IDs and keep the environment much cleaner as possible.

Gatsby - The data mentioned above is queried via GraphQL in any template and configuration file, each time a field is removed or its settings are changed, make sure that Gatsby knows it.

The list of the available languages comes directly from Dato, and the GraphQL schema for any content model gets updated everytime you add or remove new languages making possible to separate the languages management and the code itself.

Moreover, knowledge of Gatsby's createPages API and pageContext object is required in order to customize the starter. In any case, everything's perfectly explained here.


Starter installation

  1. Install the starter with Gatsby CLI:
gatsby new portfolio-blog https://github.com/https://github.com/argahv/portfolio.git
  1. Log in to your DatoCMS account and

    Clone DatoCMS project

  2. Access the project in your DatoCMS admin panel and retrieve your public API token. Replace it in your gatsby-config.js file:

    {
      resolve: 'gatsby-source-datocms',
      options: {
        apiToken: 'YOUR_API_TOKEN',
      },
    },
  3. Initialize a new Git repo, commit and push all the files.

  4. Access to Gatsby Cloud, add a new site and connect with your Git repo

Import to Gatsby Cloud

  1. Connect your DatoCMS account to Gatsby Cloud and create the site.

Connect DatoCMS


Starter configuration

1. Languages

Access the cloned project on DatoCMS and set the languages by navigating to: Settings > Environment > Settings

Drag to the left at the first position your main language, and rearrange in order of importance the other languages. Pre-existent localized fields' content will vanish once you delete the related language. Language Selection

Once a new language is added, a new tab for translations will appear for each content model.

In any query you will run, the results will always be sorted based on the aforementioned order.

"allDatoCmsBlogPost": {
"edges": [
  {
    "node": {
      "slug": "introducing-jsx",
      "locale": "en"
    }
  },
  {
    "node": {
      "slug": "introduzione-a-jsx",
      "locale": "it"
    }
  },
  {
    "node": {
      "slug": "presentando-jsx",
      "locale": "es"
    }
  },

The language switcher will show them in the same order as well.

Language Switcher

The language path for your main language won't be generated, instead it will for any other language:

https://yourgatsbywebsite.com/contact
https://youtgatsbywebsite.com/it/contatti
https://youtgatsbywebsite.com/es-ES/contactos

2. Fields relationships

Before adapting your DatoCMS environment to your needs and start editing core files, please make sure to understand the relationship between the following fields and the core files.

2a. Special content fields

Those fields are all mandatory in order to ensure a proper behavior of any multilanguage feature such as Language Switcher, SEO Meta Tags injection and navigation.
Please note that each of the following fields must always share the same ID in each content model according to its nature. Rename them only if you know what you're doing.

Field ID Field Type Localized GraphQL identifier
slug SEO > Slug Yes slug

Necessary to generate pages, retrieve corresponding content and build URLs.

  • Assigned on DatoCMS to any page/post content model that will be generated as a page with an unique slug.
  • Exported to the pageContext object during generation
  • Queried in gatsby-node.js, any template and languageSwitcher.jsx

Field ID Field Type Localized GraphQL identifier
reference Single-line string No reference

An helper, necessary to retrieve corresponding slugs for each page/post, its value should always be equal to the slug assigned to your main language.

  • Assigned to any page/post content model that will be generated as a page with an unique slug.
  • Exported to the pageContext object during generation
  • Queried in gatsby-node.js and languageSwitcher.jsx

Field ID Field Type Localized GraphQL identifier
seo SEO > Seo Meta Tags Yes seo

Necessary to inject the correct SEO tags to the <head> with React Helmet.

  • Assigned to any page/post/archive content model that will be generated as a page with an unique slug.
  • Queried in any template which will render a page/post/archive.

Field ID Field Type Localized GraphQL identifier
type_name Single-line string No typeName

Necessary to manage internal link navigation within Structured Text Fields.

  • Assigned to any content model that will be generated as a page with an unique URL. Although this field is not required to generate pages, it is mandatory for any page/post you want to allow your editors to create a link to it. It is also assigned to any block that will be rendered within a Structured Text Field field using <StructuredText /> component.
  • Queried in any template which includes a Structured Text Field.
  • Explained in detail here

2b. Special settings fields

Location: Available in the content model name Website Settings or in GraphQL as datoCmsWebsiteSetting

Fieldset Field ID Field Type Localized GraphQL identifier
Blog blog_path Single-line string No blogPath

Necessary to generate the archive page and the correct URLs for your blog posts. Since it is not a localized field, your blog path will always look like: /blog, /it/blog, /es/blog.
Please note that this field, unlikely the following one, it is mandatory to ensure a proper behavior of language switch and link navigation.

  • Assigned to the content model named Website Settings
  • Queried in gatsby-node.js, and useLanguages hook. Used in navigator.jsx, languageProvider.jsx and languageSwitcher.jsx.

Fieldset Field ID Field Type Localized GraphQL identifier
Blog posts_per_page Integer No postsPerPage

Necessary to calculate the pages of your archive pages, they will always look like: /blog, /blog/2, /blog/3

  • Assigned to the content model named Website Settings
  • Queried in gatsby-node.js
  • Exported to the pageContext object

Fieldset Localized
Global SEO Yes

All those fields are required in order to compose the title schema and set the fallbacks. They are queried in src/components/layout/pageWrapper.jsx and used to build the meta tags logic.


Fieldset Localized
PWA Yes

All those fields are required in order to generate multiple per locale webmanifest files. They are all queried in gatsby-node.js, exported to /public and also assigned to Helmet's meta tags in pageWrapper.jsx


Fieldset Localized
Branding No

All those fields are required in order to populate CSS variables values. The logo instead, will be downloaded and saved. They are all queried in gatsby-node.js, exported to src/static/settings.json - src/static/logo.svg. JSON file is imported in src/components/layout/globalStyles.js and logo in src/components/header.jsx


Fieldset Localized
Misc Yes

The following fieldset handles the localization of various strings used in the fronted such as minutes of reading or buttons. They are queried in any template or component which will display them.


3. Fallbacks

Since translations are set as mandatory on DatoCMS you won't be able to publish new content if you haven't actually translated it.

When adding new languages, a build will automatically be executed and the new language will be displayed in the switcher. If you haven't translated the content, you most likely will get errors while browsing that language pages in development mode.

If you want to get rid of the errors simply set the fallbacks in gatsby-config.js under gatsby-source-datocms:

{
  resolve: 'gatsby-source-datocms',
  options: {
    apiToken: 'YOUR_API_TOKEN',
    localeFallbacks: {
      'lt-LT': 'en',
      kr: 'en-US',
    },
  },
}

Fields' values not yet translated (returning null) will return the value of the fallback language.

If you want to be able to publish content on Dato even if it is not translated, you can uncheck the mandatory translation in the content model settings.


4. Pages generation

In order to prevent build failures, please check that:

  • In case you set up new languages not included in the starter, you will always have to fill up any field marked as required in any content model.

  • Before starting the dev server always make sure that content for any language is published and marked without any error:
    Content correctly published

  • If you want the build to succeed even if you haven't translated yet the content for new languages, set the fallbacks in gatsby-config.js and uncheck the mandatory translation as explained in the previous section.

  • In case you removed any non-mandatory content field such as heroTitle etc, make sure that they are not queried anymore in any file.

Generate pages as you normally would, the only difference is that you will always have to export the reference field to the pageContext object as well.

Remember to always name context object properties as displayed below.

A typical dynamic pages generation would look like:

  const data = await graphql(`
    query {
      allDatoCmsOtherPage {
        nodes {
          id: originalId
          locale
          slug
          reference
        }
      }
    }
  `);

  data.allDatoCmsOtherPage.nodes.forEach(({ locale, slug, id, reference }) => {
    createPage({
      path: `${
        locale === defaultLanguage
          ? `/${slug}`
          : `${locale}/${slug}`
      }`,
      component: OtherPageTemplate,
      context: {
        id,
        locale,
        slug,
        reference,
      },
    });
  });
};

5. Language switcher

The language switcher works out-of-the-box and needs no futher configuration. As long as you follow the presentation below each related field on DatoCMS, there won't be issues.

Just to make sure:

  • For any generated page, the reference field should always be exported to the pageContext object.
  • The reference field value must be equal to the value assigned to the slug of your main language
  • In case any of your page/post has the same slug for different locales, you should never set the slug field as non-localizable.
  • You can't localize the blog_path field

As soon as you add new languages, localize and publish the content, a new language will be displayed and you can start switching between different languages.


6. Editing the menu

Just edit the content model named Menu by adding/removing block items and localize the name and the corresponding slug.


7. Internal link navigation using Navigator component

When creating an internal link you need to use the built-in <Navigator /> component. It's built on top of GatsbyLink and supports any type of page included in the starter.

prop type description
article Boolean Sets the navigator to treat the provided slug as a blog post
page Boolean Sets the navigator to treat the provided slug as a page
archive Boolean Redirects to the archive page
home Boolean Redirects to the homepage page
to String Accepts the slug queried from GraphQL, needed only for articles and pages
className String Your own className
ariaLabel String Accessible link name

7a. Usage - Case A - Providing a specific slug

Used in /src/templates/home.jsx

You are creating a button in the homepage hero section to redirect to the Guide page. You are not using Structured Text, you are just creating the link directly in the template file.

A typical usage would look like:

import Navigator from '../components/langHelpers/navigator';

...

<Navigator
  className="classicButtonOutline"
  page
  to={data.guidePageLink.slug}
>
  {data.guidePageLink.title}
</Navigator>

...

export const query = graphql`
  query HomePageTemplate($locale: String!) {
    guidePageLink: datoCmsOtherPage(
      locale: { eq: $locale }
      reference: { eq: "guide" }
    ) {
      slug
      title
    }
  }
`;

Caveat: You will have to manually enter the reference field value of the corresponding post/page. It can be done within 10 seconds but use it only if your content editors are not going to control this slug.


7b. Usage - Case B - Dynamic rendering with <StructuredText />

Used in /src/templates/article.jsx

You are creating a post template which content will be managed by your editors. They will decide which links to display in the page. You are using Structured Text.

Since DatoCMS provides a set of components to work faster with React, it is possible to automate the rendering of any link or block included in a Structured Text Field with a custom component. The complete documentation can be found here.

This is so far the best approach because you will set the template only once, then your content editors will add new articles and create links without no further intervention on your behalf.

Output field - structured_body field settings (in the article or page content model where links will be added):

Input fields

type_name field settings (in "Blog Posts" content model):

type_name field value (when creating/editing an article):

Content editing

A typical usage would look like:

import { StructuredText } from 'react-datocms';
import Navigator from '../components/langHelpers/navigator';

...

  <StructuredText
    data={data.datoCmsBlogPost.structuredBody}
    renderLinkToRecord={({ record, children, transformedMeta }) => {
      switch (record.typeName) {
        case 'page':
          return (
            <Navigator {...transformedMeta} page to={record.slug}>
              {children}
            </Navigator>
          );
        case 'article':
          return (
            <Navigator {...transformedMeta} article to={record.slug}>
              {children}
            </Navigator>
          );
        case 'archive':
          return (
            <Navigator {...transformedMeta} archive>
              {children}
            </Navigator>
          );
        case 'home':
          return (
            <Navigator {...transformedMeta} home>
              {children}
            </Navigator>
          );

        default:
          return null;
      }
    }}

...

export const query = graphql`
  query BlogPostTemplateQuery(
    $id: String!
    $locale: String!
  ) {
    datoCmsBlogPost(originalId: { eq: $id }, locale: { eq: $locale }) {
      structuredBody {
        value
        links {
          ... on DatoCmsBlogPost {
            typeName
            slug
            id: originalId
          }
          ... on DatoCmsOtherPage {
            typeName
            slug
            id: originalId
          }
          ... on DatoCmsHomepage {
            typeName
            id: originalId
          }
          ... on DatoCmsArchivePage {
            typeName
            id: originalId
          }
        }
      }
    }
  }
`;

Basically, renderLinkToRecord will check for the value assigned to the type_name field of your linked record, if it matches the case, it will render it using <Navigator /> with the right prop. Just remember to always name the field with same ID (type_name) for each content model or block or the switch statement won't work.

Caveats:

  • You will always have to query originalId with the alias id for each fragment (content model) or the rendering will fail.

  • For any page rendered by an unique template, you will have to add the fragments manually to the template query and add the content model to the allowed record links for each structured text field.

  • I personally advise to force your content editors to choose the type_name value from a list of specific values in order to prevent query errors or typos.


7c. Usage - Other cases

A redirect to the homepage would look like:

import Navigator from '../components/langHelpers/navigator';
import { LogoIcon } from '../components/vectors/logo';

...

<Navigator home>
  <LogoIcon />
</Navigator>

...

A redirect to the archive page would look like:

import Navigator from '../components/langHelpers/navigator';
import { BackToArchiveButton } from '../components/buttons';

...

<Navigator archive>
  <BackToArchiveButton />
</Navigator>

...

8. Creating new templates

When creating new templates (e.g. single pages), always import and wrap your template around the PageWrapper component. Always pass to the pageData prop the entire pageContext object:

...

import PageWrapper from '../layout/pageWrapper';

const BlogPostTemplate = ({ data, pageContext }) => {
  return (
    <PageWrapper pageData={pageContext} ...

DatoCMS recently released Structured Text Fields, in my opinion this is a big stride for content editors. Basically, they will be able to create pages and build the entire layout by using blocks which will be rendered by your components.

In the next section, there's an example on how to use custom components to render blocks inserted inside Structured Text Fields.


9. Blog features

Instead of writing blog articles in markdown, you can take advantage of Structured Text Fields and give your content editors a Medium-like fullscreen writing experience:

Structured Text Focus Mode

Please take note that this starter only supports Structured Text Fields to handle blog posts content and if you want to stick with the markdown approach (by using a multi-line text field), internal navigation and synthax highlighting won't work.

9a. Archive pagination

Archive pagination works out-of-the box and needs no further configuration besides setting the number of posts per page on Dato. I have used the same logic explained here by Gatsby folks.

9b. Prev/next navigation

The prev/next navigation works out-of-the-box as well.

In case you want to apply it to a new collection of records, keep in mind that:

  • A counter must be initialized in each loop which generates single post pages for each locale.
  • A skipNext variable should be exported to the pageContext object for each post/record you are generating and its value must be equal to the counter value, and it should be equal to zero for each last generated post for each locale.
  • A skipPrevious variable should be exported as well, its value should be equal to (counter - 2) except that for the first generated post which must be equal to 1.

In order to have a navigation coherent with your stream, always use the same sort parameter used in gatsby-node.js, also in any other template which contains the post stream and in the single post template.

gatsby-node.js

query {
  allDatoCmsBlogPost(sort: { order: ASC, fields: [locale, meta___firstPublishedAt] }{
    edges {

home.jsx

query {
  allDatoCmsBlogPost(sort: { order: ASC, fields: meta___firstPublishedAt }
    edges {

article.jsx

export const query = graphql`
  query BlogPostTemplateQuery(
    $id: String!
    $locale: String!
    $skipNext: Int!
    $skipPrevious: Int!
  ) {
    next: allDatoCmsBlogPost(
      filter: { locale: { eq: $locale } }
      sort: { order: ASC, fields: meta___firstPublishedAt }
      limit: 1
      skip: $skipNext
    ) {
      edges {
        node {
          slug
          title
        }
      }
    }
    previous: allDatoCmsBlogPost(
      filter: { locale: { eq: $locale } }
      sort: { order: ASC, fields: meta___firstPublishedAt }
      skip: $skipPrevious
      limit: 1
    ) {
      edges {
        node {
          slug
          title
        }
      }
    }

Then, just use the queried post values to build the prev/next component render logic.

The complete logic to extract the counter variable starts in gatsby-node.js at line ~170 and the prev/next render logic can be found in article.jsx.

9c. Synthax highlighting

It works out-of-the-box and needs no further configuration, all you need to do is to add code to the Structured Text Field and choose the language.

By using react-datocms' <StructuredText /> component, I set a custom render rule for the code using the amazing react-synthax-hightlighter to style the code synthax.

If you wish to use another synthax highlighter package, you can set your own render rule by following react-datocms' documentation here.

To change the theme, simply import your favourite style. Complete documentation can be found here.

import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { atomDark } from 'react-syntax-highlighter/dist/esm/styles/prism';

9d. Images

Simply add the Article Body Image block to your Structured Text Field article body, and assign to the Type Name field to the only value you can choose.

Image Block

Article Body Image Block

I have created only this block to handle articles' images, but you can always create many blocks as you want and set a your own component as renderer.

    renderBlock={({ record }) => {
      switch (record.typeName) {
        case "image":
          return (
            <BodyImg
              image={record.image.gatsbyImageData}
              alt={record.image.alt}
            />
          );
        default:
          return null;
      }
    }}

Don't forget that with Structured Text Fields you can build the entire layout of the page, all you need to do is to set the your custom renderers for the blocks.

9e. Article Body Styles

All the article body substyles such as h1, h2, ul etc, can be found and customized in /src/layout/paragraphStyles.js. The styled component named ArticleBody styles all the HTML elements you can find inside the structured text field, basically, it is just the wrapper of the <StructuredText /> component.

You will also find a template literal variable called CommonStyles which holds all the substyles shared with the other component named Paragraph which is just another structured text wrapper used in other pages such as Features.

9f. Social sharing

It works out-of-the box and needs no further configuration, you can customize the appearance in /src/components/ui/articleHeader.jsx.

8g. Miscellanous

By using DatoCMS relationship fields, each article can have its own Author assigned, you can manage them in the content model named Authors and then assign to each post an Author.


10. SEO

10.1 Configuring Global SEO

Access the Website Settings content model and set the Global SEO fields.

Global SEO 1

Global SEO 2

site_url field must always be updated everytime your domain changes, social sharing depends on it as well.

archive_name and page_name are used to construct the title meta according to the behavior shown at the beginning of the readme.

Once you have updated and localized the fields according to your content, you can configure SEO for any content model which will be generated as a page.

10.2 Manage SEO

DatoCMS provides a localizable SEO Field which can be considered as a fieldset containing other SEO specific fields, in particular:

title description image

All you need to do is to fill in those three fields. Your vital meta tags will all be generated properly and will display your website in the search results and the social cards as shown in the beginning of the readme.

Each template is wrapped in a component named <PageWrapper /> which accepts three SEO-related props:

prop type description
seoTitle string Accepts the title defined in the seo field on any content model
seoDescription string Accepts the description defined in the seo field on any content model
seoImage string Accepts the image url uploaded in the seo field on any content model

If your related content model has a SEO field set, you should definitely set these props and query the related fields.

const HomePageTemplate = ({ data, pageContext }) => {

  return (
    <PageWrapper
      pageData={pageContext}
      seoTitle={data.datoCmsHomepage.seo.title}
      seoDescription={data.datoCmsHomepage.seo.description}
      seoImage={data.datoCmsHomepage.seo.image.url}
    >

    ...


export const query = graphql`
  query HomePageTemplate($locale: String!) {
    datoCmsHomepage(locale: { eq: $locale }) {
      seo {
        title
        description
        image {
          url
        }
      }

...

Caveats:

  • If you don't want to localize SEO for some content you're going to create, simply do not add any SEO field to the content model, as long as the field is not queried in your template and props are not set, fallback settings will always be applied and the build will never fail.

  • Any SEO field should be set as required, however, this means that its image field will require you to pick an image, even if you want to use the fallback one. Since you won't be allowed to publish the content if the image field is empty, you should assign a random image and then, query only the title and the description and assign only those two props (as I did in src/templates/home.jsx). The fallback image will automatically be applied.

  • Alt tags for your images will always be localized as long as your image field has been set as localizable in your content model.

  • A sitemap will automatically be generated on build time by gatsby-plugin-sitemap, all you need to do is to write your website address in the siteMetadata object in gatsby-config.js


11. PWA

PWA support for any configured languages works out-of-the-box. All you need to do is to edit and localize the related settings fields:

PWA Settings

As soon as you will add a new language on Dato and localize its PWA settings, a new webmanifest file named manifest_<lang-code>.webmanifest will be exported to the /public folder and dynamically injected to the <head>. Instead, the default language webmanifest will always be named manifest.webmanifest.

PWA Icon Webmanifest files

Favicon will be generated automatically, based on the above icon. PWA meta tags are already inserted manually to the <Helmet /> component in /src/components/pageWrapper.jsx.

If you wish to customize the manifest JSON schema, you can edit the manifest object variables in gatsby-node.js, keep in mind that icons are downloaded in different sizes by using different imgixParams and aliases in the dedicated query in gatsby-node.js.


12. Redirect

When a redirect takes place?

Once the user's preferred language has been evaluated and stored to localStorage, a redirect takes place when the user visits the homepage in default language ("/") via direct access (refresh / link).

When a redirect doesn't take place?

If the user's preferred language is equal to the website default language ("/") no redirect will take place.

How the preferred language is evaluated?

When the user visits the homepage ("/") for the very first time, we try to find a match among the app's languages and the browser available languages:

  • Browser Languages: ["de-CH", "en-US"]

  • App Languages: ["fr", "en", "de"]

In such case de is stored as preferred_lang in localStorage and the user is redirected to ("/de"). The language is evaluated according to the order priority of the browser (user) languages.

When no languages are specified in the browser languages list, the system default language will be used to evaluate the preferred language.

If there is no match, the app's default language will be stored as the preferred one.

How the preferred language is updated?

  • When the user accesses any page in a secondary language ("/es/guia") or a page in default language ("/guide") via direct link, we assume that the user wants to set that language as the preferred one.

  • When the user switches the language from the switcher a new preferred_lang value is stored/overwritten in localStorage

If you want to disable the redirect, just remove the <Redirect> component in gatsby-browser.js.

import Redirect from './src/components/langHelpers/redirect';
import GlobalStyles from './src/components/layout/globalStyles';

export const wrapPageElement = ({ element }) => (
  <>
    <GlobalStyles />
    <Redirect /> // <- Remove
    {element}
  </>
);

Wheter or not you have set the <Redirect /> component, the preferred language will always be stored/overwritten once the user switches the language from the switcher.


13. 404

If user never visited the website before and tries to access to a non-existent page, the behavior follows the same logic of the homepage redirect, the preferred language is evaluated and content is displayed in the correspondent language.

In case the user will try to access a non-existent page in the future, content in preferred language will be displayed and the back to home button will redirect to the correspont homepage.

Content for 404 page can be localized on DatoCMS in the content model named 404 Page.


14. Styling

By keeping the default starter configuration, CSS color variables and logo can be configured directly on DatoCMS without no further intervention on the core files.

Data is queried in gatsby-node.js and saved to /src/static during build time. Then the JSON file containing the hex color codes is imported in src/layout/globalStyles.js and values are assigned direct to CSS variables.

import { createGlobalStyle } from 'styled-components';
import Colors from '../../static/colos.json';

const GlobalStyles = createGlobalStyle`

        :root {
            --primaryColor: ${Colors.primaryColor};
            --primaryDark: ${Colors.primaryDark};
            --primaryLight: ${Colors.primaryLight};
...

The SVG logo url instead, is queried with useStaticQuery in src/components/ui/header.jsx.

If this approach doesn't fit your needs, you can safely remove the first block of code in gatsby-node.js commented as "Colors". Then, remove the imports in createGlobalStyles.js and header.jsx. Optionally delete the related fields on Dato as well.


15. Issues

Please do not hesitate to open an issue by attaching your build log/errors.

About

My Personal Blog Site based on GatsbyJS ( SSG ) and DatoCMS

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published