Welcome to the official code repository for the cynsar.foundation
website. This platform was meticulously developed using DatoCMS.
We're grateful to Cynsar Capital for their generous donation of a privacy-preserving page tracking code, ensuring our visitors' data is protected and anonymous.
- DatoCMS Integration: Efficient content management that ensures the website's content is up-to-date and seamlessly displayed.
- Privacy-Preserving Page Tracking: Courtesy of Cynsar Capital, this tracking code guarantees our visitors’ privacy is always a top priority.
- Headless Gatsby Starter Template: Headless Multilanguage Starter for Gatsby all thanks and all the code is borrowed from this Live Demo
# Clone the repository
git clone https://github.com/Cynsar-Foundation/spaces.git
# Navigate to the project directory
cd cynsar.foundation
# Install dependencies
npm install
# Start the local development server
npm start
We welcome any contributions to improve the cynsar.foundation
platform. Please read the CONTRIBUTING.md before pushing any changes.
This project is licensed under the MIT License - see the LICENSE.md file for details.
Thank you for taking an interest in the Cynsar Foundation. Your support and contributions further our mission.
Further Readme
The most powerful multilanguage blog starter for Gatsby. Completely headless.
- 100% Headless: Define languages and translate pages, posts, slugs, SEO meta tags and PWA settings directly on DatoCMS.
- Language switcher component swapping between different slugs/paths per locale
- Automatic and easy internal links localization using custom Navigator component
- User preferred language detection and redirection
- Built-in support for RTL languages such as Arabic or Hebrew
- Per-locale PWA webmanifest files generation on build time, dynamically injected according to the page locale.
- Support for any language code path such as "/en-GB" or "/en"
- 404 page displaying localized content according to the user preferred language
- Choose which post or category to translate (and generate) for each locale.
- Related posts, social sharing and synthax highlighting.
- Dark mode with CSS variables
- Built without any internationalization plugin, just Gatsby APIs.
Performance | Accessibility | Best Pratices | SEO | PWA |
---|---|---|---|---|
98 ~ 100 | 100 | 100 | 100 | 8/9 |
- Important notes
- Configuration
- Managing content
- Styling
- SEO
- PWA
- Redirect
- 404 page
- RTL text direction support
- Extending the starter
- Troubleshooting
- Issues
It is the most powerful headless CMS out there and also, the only one up to the job. You can define app languages directly in the project administration area and query them via GraphQL as well. Moreover, it has dozens of features that give your content editors superpowers.
Don't worry about the pricing or any other plan restriction, as long as your website will have less than 300 pages (for each locale 😊) you will never exceed the free developer plan. There are no limits on the locales you can configure in your project.
if you are not familiar with DatoCMS, the following links will be useful:
Also, you can enjoy instant previews on Gatsby Cloud, without any crazy futher configuration, just save the draft and preview the content.
⚠️ This project is not mantained by DatoCMS folks neither I am affiliated with them.
This starter lets you completely focus on your content by taking care of all the rest.
If you landed on this repo and your goal is just to setup a multilanguage blog with the same content structure of this starter (articles, category pages and archive pages) all you have to do is to follow all the steps of the configuration section.
The 95% of the whole process is done directly on DatoCMS in a super-friendly environment.
Once new content is translated and published, your website will build accordingly and all the features will work out-of-the-box (page generation, language switcher, internal link navigation, SEO meta tags, PWA webmanifest, redirect, right-to-left text support...).
If you want to deeply customize it, like generating pages with different paths, you can visit the section Extending the starter.
In such case, knowledge of GraphQL query variables and Gatsby createPages API is required.
- Install the starter with Gatsby CLI:
gatsby new headless-gatsby-multilang https://github.com/smastrom/headless-gatsby-multilang.git
-
In the cloned project administration area, grab the read-only API token and replace it in your gatsby-config.js file:
{ resolve: 'gatsby-source-datocms', options: { apiToken: 'YOUR_API_TOKEN', }, },
-
Initialize a new Git repo, commit and push all the files.
-
Sign-in to Gatsby Cloud, add a new site and connect with your Git repo:
- Connect your DatoCMS account to Gatsby Cloud and create the site.
Please make sure that the development server is not running before following these steps.
On DatoCMS navigate to: Settings > Environment > Settings and check for the starter default languages:
If your website final language list won't include any of the fully translated starter language codes (en
, es-ES
or it
), pick one of your choice (for example en
) and do not delete it, just keep it in the list (for the being) and delete the others.
Otherwise, just delete the unnecessary languages, leave the ones you will use and pick one of your choice.
Starting from now this is your fallback language.
This starter is using fallbacks for
ar-AE
, so do not consider it as a language you can pick.
⚠️ If for example, you are planning to addEnglish (United Kingdom) "en-GB"
and to remove"en"
, consider"en-GB"
as a different language and keep"en"
in the list as fallback.
Choose any kind of lang code you prefer from the dropdown and add the languages:
Drag to the left at first position your website default language, and rearrange in order of importance the others:
💾 Save your project settings.
Open gatsby-config.js and set the fallbacks as displayed below:
{
resolve: 'gatsby-source-datocms',
options: {
apiToken: '<YOUR_PUBLIC_API_TOKEN>',
localeFallbacks: {
'zh': 'en',
'pt-BR' : 'en',
},
},
},
You should create a property for each new language (not included in the starter) and assign to it the fallback picked before ("en").
💾 Save the file.
Navigate into your project folder and run gatsby develop
. Check on your terminal, at some point you will see something like:
By opening http://localhost:8000 you will notice that your languages are now displayed in the switcher, in the same order as you set before in your administration area:
As you can see pages are generated with proper paths, SEO meta tags and the webmanifest localized are injected to the head for each language.
Whenever you will change the languages order on Dato, your website will be generated accordingly.
You can switch language and navigate the website. Content will still be displayed in fallback language.
Commit and push your changes. Access Gatsby Cloud and monitor the build trigged by your Git connection, it should be ready soon.
Once the build is completed, if you visit your website (via the free domain assigned by Gatsby Cloud) you should be able to see your website live just like you see it locally.
A build will also be triggered by the DatoCMS connection everytime you publish content.
Before finalizing the starter configuration make sure to have read and understood this section.
I have organized the vertical menu of the content editing area according to the content models' type and purpose:
Gatsby Node
models are queried by gatsby-node.js in order to generate pages and also by the templates (used to generate such pages) in order to render the correspondent data fields.
Data-only
models are queried in different JSX files to render some data but they are not used to generate any page (hence the name data-only).
Basically, what differs between the two groups is that Gatsby Node
models have a localizable slug
field used to build the path and a localizable seo
field to render the meta tags.
Single instance
Homepage - Used to generate the homepage:
- Default language:
/
- Other languages :
/it-IT
Categories Archive - Used to generate the categories archive page:
- Default language:
/categories
- Other languages :
/it-IT/categorie
Blog Root - Used to generate the blog root page:
- Default language:
/blog
- Other languages :
/it-IT/articoli
Collections
Other pages - Used to generate pages using a shared template such as /guide
and /features
- Default language:
/guide
- Other languages :
/it-IT/guida
Categories - Used to generate category pages:
- Default language:
/blog/react
- Other languages :
/it-IT/articoli/react
Posts - Used to generate post pages:
- Default language:
/blog/react/rendering-elements
- Other languages :
/it-IT/articoli/react/renderizzare-elementi
Collections
Authors - A collections of records representing the authors of your blog. These records can be linked to any post.
Single instance
Menu - In this content model you can add or remove a menu item. You can order them via drag-n-drop and link to each of them a record of the Gatsby Node group.
Footer - Contains the footer content.
404 Page - 404 page content, fully localizable.
Seo and PWA - Fields related to Global SEO and PWA webmanifest.
Misc strings - String fields used site-wide such as "Recent posts" or "See all categories".
Make sure your development server is not running.
- Delete any article / category / author record coming with this starter (do not delete the content models!).
- Create and publish your first category and author records and translate the fields for any locale.
- Create and publish your first post, translate the fields for any locale and link the category and the author just created to the post.
⚠️ Since the fallback language picked before is still necessary to finalize the configuration, translate also its fields. You can copy-paste from another language or enter some dummy data, it doesn't matter. It will take 30 seconds.
- Run
gatsby develop
and test the outcome
From now on when you will add a category or a post (or you will edit it) the layout will change accordingly and new pages with proper paths will be generated as well. Usually, it takes 2 to 4 seconds to see the edits live on your dev server.
Translate any other content models' record and field such as Homepage
, Categories archive
, Blog root
, Global SEO and PWA
, Misc text strings
etc...
You will always notice which records needs to be translated:
❌ | ✅ |
---|---|
⚠️ You have to follow this step only if you had to pick as fallback a starter language that won't be included in your website language list.
- Once fields' content has been translated in any language you added, navigate to your administration area and delete the fallback language picked before from your languages list:
- Open gatsby-config.js and delete the fallback properties:
{
resolve: 'gatsby-source-datocms',
options: {
apiToken: '<YOUR_PUBLIC_API_TOKEN>',
localeFallbacks: { // <-- Delete from here
zh: 'en',
'pt-BR' : 'en',
}, // <-- to here
},
},
If your plan is to publish content in default language and to translate other languages later on, set the fallbacks in gatsby-config.js:
{
resolve: 'gatsby-source-datocms',
options: {
apiToken: '<YOUR_PUBLIC_API_TOKEN>',
localeFallbacks: {
'pt-BR': 'zh',
},
},
},
In this way pages generation won't fail for untranslated records since any untranslated field queried via GraphQL will return the value of the fallback language you just set.
😉 By now, you should be able to add and translate posts, categories and any other record you want and the starter will take care of the rest. Check the next section to have a complete overview on how to handle blog content.
When you edit records of Posts
and Categories
content models, you will notice this boolean field:
By setting it to true
, the post or category page won't be generated for that language.
This is an example of category not available for "es" language.
This is an example of post not available for "it" and "es" languages.
If for example, the field is set to true for "es" language and the user is viewing that post/category in "en", if he tries to switch to "es", he will be redirected to the blog root.
Same happens for categories.
⚠️ If you set this field to true for a category, all the posts linked to that category won't be generated as well for that language, no matter if you have translated them.
⚠️ I decided to not enforce field validation onslug
field for Posts and Categories. Before publishing any content always check that such fields are populated for any locale you want to generate the page (unless you have set a fallback).
Simply access the content model named Menu
under the Data-only
group:
Add a new "Menu Link" item and link the record you want to redirect the users. You can change the order of the block items by drag-and-drop and choose to set a complete different menu for each locale.
In each post record you see find this boolean field:
If set to true
it will display that post in the homepage. You can select up to 6 posts, they will be ordered according to the update time.
For each menu item there's this boolean field:
If set to true
it will render the categories dropdown in both desktop and mobile menus.
If you want to change the order, simply sort the records in the Categories
content model via drap-and-drop:
⚠️ By default the dropdown will display the first six categories of your blog and a link at the bottom to redirect the user to the categories archive page. If you want to increase the number of categories shown, you can check this section.
Each category page and the blog root page, displays a scrollable menu where you can navigate the categories:
If you want to change the order, simply rearrange the records order as explained in the previous section.
Once your categories number will increase and overflow the menu, a scrollbar with a gradient will appear:
Edit the category_link
field in each post by selecting a category from the dropdown:
As soon as you add a new category it will be available for selection and your page paths generated accordingly.
You can also decide to not assign a category to a post, simply that post won't be rendered inside the category pages and the card won't display the category box:
The same happens for the authors, just add a new record in the Authors
content model and update the record.
This is an example of uncategorized post.
You can choose up to two posts to display below the article body:
All you have to do is to select the records in the link field of the Post
content model.
If no posts are set, the section won't be displayed.
Navigate to src/components/Layout/sharedStyles/globalStyles.js
and change the css variables values for the two following classes:
.lightTheme {
--primaryColor: #0067fa;
--headingsColor: #4d4d4d;
--baseTextColor: #6e7581;
--dividerColor: #e2e2e2;
--markBackgroundColor: #fdffb4;
--markTextColor: #4d4d4d;
--inlineCodeTextColor: #4d4d4d;
--inlineCodeBackgroundColor: #dbefff;
--backgroundColor: #ffffff;
--backgroundTransparentColor: rgba(255, 255, 255, 0);
--backgroundColorAlt: #ffffff;
--codeBlockBackgroundColor: #181b22;
}
.darkTheme {
--primaryColor: #5995ea;
--headingsColor: #eeeeee;
--baseTextColor: #aaaaaa;
--dividerColor: #242a31;
--markBackgroundColor: #b2dbff;
--markTextColor: #181b22;
--inlineCodeTextColor: #ffffff;
--inlineCodeBackgroundColor: #293b4a;
--backgroundColor: #181b22;
--backgroundTransparentColor: rgba(24, 27, 34, 0);
--backgroundColorAlt: #1d2028;
--codeBlockBackgroundColor: #181b22;
}
If for any reason CSS variables are not enough to style both themes, you can import the useTheme
hook:
import { useTheme } from 'src/hooks/useTheme';
And use it in any component / template:
const { isDark } = useTheme();
return <Header>{isDark ? <LogoBlack /> : <LogoWhite />}</Header>;
You can import and replace the logo in src/components/Layout/Header/Full
.
Please check this issue for a guide on how to change the default (safe) fonts.
By using react-datocms' <StructuredText />
component, a custom render rule for the code
node has been set using the amazing package react-synthax-hightlighter.
To change the theme, simply replace the import in src/templates/Article.jsx
with your favourite style. Complete documentation and styles reference can be found here.
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { atomDark } from 'react-syntax-highlighter/dist/esm/styles/prism'; // <-- Import your favourite style
If you want to override some styles from the original theme, you can do that by editing css rules in src/components/Layout/Blog/ArticleBody/styles.js
.
If you wish to use another synthax highlighter, you can set your own renderNodeRule
by following react-datocms documentation.
Access the Website Settings content model and set the Global SEO fields.
Once you have updated and localized the fields according to your content, you can edit any seo
field of any content model of the Gatsby Node
group.
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. The sitemap will be available at https://yourwebsite.com/sitemap/sitemap-0.xml
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 in the content model named SEO and PWA
:
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
.
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, bear in mind that icons are downloaded in different sizes by using different imgixParams
and aliases in the dedicated query in gatsby-node.js.
When the user visits the homepage in default language ("/") a redirect will always take place if the language included in the Accept-Language
request header matches one of the website's secondary languages.
The redirect will be performed server-side and not in the browser.
When starting the development server you will notice a warning like:
There are routes that match both page and redirect. Pages take precedence over redirects so the redirect will not work:
- page: "/" and redirect: "/" -> "/it/"
- page: "/" and redirect: "/" -> "/es-ES/"
- page: "/" and redirect: "/" -> "/ar-AE/"
Do not worry, the redirect will take place as expected 😃
Please take note that the redirect will work only once the website has been deployed to Gatsby Cloud and not during local development.
If for any reason you want to disable the redirect you can remove the lines ~45 to 60 in gatsby-node.js.
Content for 404 page can be localized on DatoCMS in the content model named 404 Page
.
Unfortunately, Gatsby doesn't allow to server-side render the 404 page on each request. Such page must be statically generated on each build.
However, content will always be rendered in correct language by following a different approach (in the browser):
Case 1 - Visiting a non-existent page for the very first time
Assuming that the user never visited the website and tries to access a non-existent page, we try to find a match among the website languages and the browser available languages.
Example:
-
Browser Languages:
["de-CH", "en-US"]
-
Website Languages:
["fr", "en", "de"]
In such case de
is considered as the user's preferred language and they will see the correct localized content in German.
If there is no match, content in website default language will be rendered.
The preferred language is always evaluated according to the order priority of the browser (user) languages.
Case 2 - Visiting a non-existent page after having already browsed the website
When the user switches the language a new preferred_lang
value is stored/overwritten in localStorage
and it will be used to render the correspondent localized data for the 404 page.
If the user never switched the language, we assume their preferred language is the website default language and content will be rendered accordingly.
This starter has built-in support for any right-to-left language such as Hebrew or Arabic.
All you need to do is to add the language code to your DatoCMS languages list, set the fallbacks and start the dev server as explained above. If the language code matches one these lang codes, once you switch the language, the layout direction will change accordingly.
It doesn't matter if you choose "ar" or "ar-AE", the correct language direction will always be identified and injected to the <html>
dir attribute.
If for any reason you need to deeply style and change elements behavior according to the language direction, you can import the useTextDirection
hook:
import { useTextDirection } from '../../hooks/useTextDirection';
And use it in any component / template:
const { isRtl } = useTextDirection();
return <span className={isRtl ? 'menuRight' : 'menuLeft'} />;
If you are ok with the content structure of the starter you most likely won't need to extend it and follow those steps. In any case everything is explained below.
99% of the times that an issue occurs is because the fields or content models queried in the project files are not available anymore on DatoCMS. Gatsby can't find them and throws an error.
This can happen because:
- You renamed the field ID
- You deleted the field
- You renamed the content model ID
- You deleted the content model
Without deleting the field or renaming the field Id from the project files.
For example, if you don't want the author
field of the content model blog_post
to be displayed in your post pages:
1. Remove from any query the author
field node:
export const query = graphql`
query BlogPostTemplateQuery(
$id: String!
$locale: String!
) {
datoCmsBlogPost(id: { eq: $id }, locale: { eq: $locale }) {
title
subtitle
author { // <--- Delete from here
authorName: name
}
} // <--- To here
...
2. Delete any destructured prop:
const BlogPostTemplate = ({
data: {
datoCmsBlogPost: {
title,
subtitle,
author: { // <-- Delete from here
authorName,
}, // <-- to here
...
3. Then remove any expression/variable displaying the data you were querying:
return (
<Header>
<AuthorDateContainer>
<Author>{authorName}</Author> {/* <-- Delete this */}
...
By taking advantage of your code editor search you can easily check if the field is still queried or rendered anywhere.
4. Once done that, you can safely delete the field from the content model on DatoCMS.
⚠️ The only field you should never delete or rename the Id is theslug
one. It is used to build the paths and by deleting/renaming it gatsby-node will throw an error. For any other field as long as you understood its purpose, you can safely get rid of it.
If for example you want to remove any page included in the starter follow these steps:
- Identify the related content model on Dato and check where its fields are queried in the project files.
If the content model has this ID on Dato: other_page
- The GraphQL fields to identify would be:
datoCmsOtherPage
allDatoCmsOtherPage
Hence the datoCms
prefix and the snake_case converted to camelCase.
-
With you code editor search for both field names among your project files and delete any prop destructuring, query or prop assignment.
-
Access
gatsby-node.js
and delete the block containing the query, the loop and the template declaration. You will find them grouped and preceded by a comment. -
Delete the template used to generate such page.
If you wish to generate pages with your own path structure, the best thing you can do is to follow the comments in gatsby-node.js
file.
The process is very quick and in short, all you have to do is to:
- Create a content model on DatoCMS
- Define and localize the
slug
field - Define and localze a
seo
field - Define and localize all content fields
- Define and localize the
- Create a dummy JSX template in
src/templates
just to test page generation - Generate pages in
gatsby-node.js
using the dummy template and by querying the content modeloriginalId
andslug
. - Once pages and paths are generated properly, edit and build the template by querying and filtering the correct data.
As soon as pages are generated correctly you can switch between languages as this feature is already set up.
When creating new templates, always import and wrap your template around the PageWrapper
component.
Pass to the pageData
prop the entire pageContext
object:
import { PageWrapper } from '../Layout/PageWrapper';
const BlogPostTemplate = ({ data, pageContext }) => (
<PageWrapper pageData={pageContext}>
...
Then any child component will have access to the page locale exported to the pageContext object during build time by gatsby-node.js
. This is required in order to render proper links in any Navigator
component and in LanguageSwitcher
as well.
When you need to render localized paths you have to use the built-in Navigator component. It's built on top of Gatsby Link and supports any prop you would normally add to it such as activeClassName
.
There are only two props in addition:
Prop | Description | Required |
---|---|---|
recordId | The originalId field value of the record used to generate the page | Yes |
passRef | In case you need to pass a ref | No |
The component will take care of retrieving and render the proper path according to the page language.
Import the Navigator component in any of your template/component:
import { Navigator } from '../../../Navigator';
Query the originalId
field of your record or content model (if a single instance) with GraphQL. You can use useStaticQuery
as well:
export const query = graphql`
query HomePageTemplate($locale: String!) {
datoCmsBlogRoot(locale: { eq: $locale }) {
id: originalId
title
}
...
Then pass the field value to the prop recordId
:
<Navigator recordId={data.datoCmsBlogRoot.id}>
{data.datoCmsBlogRoot.title}
</Navigator>
That's it. Now in any page the link is displayed with the proper path according to the page language.
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 records and create links with no further intervention on your behalf.
Of course this is already set up for you in the starter templates but in case you want to create new ones, those are the steps you have to follow:
- Add a structured text field to your content model:
In the field settings specify from which content models the editors are allowed to add links:
Links will be available for selection to your content editors when writing posts:
- Query the structured text field:
export const query = graphql`
query BlogPostTemplateQuery(
$id: String!
$locale: String!
) {
datoCmsBlogPost(originalId: { eq: $id }, locale: { eq: $locale }) {
structuredText {
value
links {
... on DatoCmsBlogPost {
id: originalId
}
... on DatoCmsOtherPage {
id: originalId
}
... on DatoCmsHomepage {
id: originalId
}
... on DatoCmsBlogRoot {
id: originalId
}
}
value
}
...
⚠️ You will always have to query theoriginalId
field with the alias id for each fragment representing the content models you allowed to be linked in the Structured text field:
- Import both StructuredText and Navigator in your template/component:
import { StructuredText } from 'react-datocms';
import { Navigator } from '../components/LanguageHelpers/Navigator';
- Use Navigator as renderer for the
renderLinkToRecord
prop and pass the id field to it:
const BlogPostTemplate = ({ data, pageContext }) => (
<PageWrapper pageData={pageContext}>
<ArticleBody>
{data.datoCmsBlogPost.structuredText?.value && (
<StructuredText
key={id}
data={structuredText}
renderLinkToRecord={({
record,
children,
transformedMeta,
}) => (
<Navigator {...transformedMeta} recordId={record.id}>
{children}
</Navigator>
)}
/>
)}
</ArticleBody>
...
Spread
{...transformedMeta}
to render any custom attribute you set on Dato when adding the link. Of course you can localize those attributes as long as the Structured Text field is set as localizable.
Example case: You are going to create three pages with different layout and content:
/contact, /about, /mission
.
You create a single instance content model for each page with its own data fields or blocks, and then query, filter and render those fields in three different templates like ContactPage.jsx
, AboutPage.jsx
and Mission.jsx
.
The template file will render the data of each single field of the content model:
const ContactPageTemplate = ({ data, pageContext }) => (
<PageWrapper pageData={pageContext}>
<Heading>{data.datoCmsContactPage.title}</Heading>
<SubHeading>{data.datoCmsContactPage.subtitle}</SubHeading>
<Hero
title={data.datoCmsContactPage.hero.heroTitle}
subtitle={data.datoCmsContactPage.hero.heroTitle}
>
</PageWrapper>
);
export default ContactPageTemplate;
export const query = graphql`
query ContactPageQuery() {
datoCmsContactPage(locale: $locale) {
title
subtitle
hero {
heroTitle
heroSubtitle
}
}
}
`;
Then you repeat those steps for any other page like /about
and /mission
.
So at the end of your journey you will have three different single-instance content models on Dato, three different templates in your project folder and three different queries for each template.
This approach has been used to generate the homepage, the categories archive page and the blog root page of this starter.
Instead of creating a single-instance content model for each page, you can create one content model of type collection, add the mandatory shared fields such as seo
and slug
and then add a single Structured text field to hold all your content.
Then you can create one record for each page you want to generate and inside each Structured Text field add different blocks according to the page layout and content.
Once your records are ready with different content per Structured Text field you can set custom renderers inside the StructuredText component in a single shared template file.
At the end of this journey you will have, one template for all the pages you are going to generate, one query to retrieve all different data and one model of type collection.
This approach has been used to generate the
/guide
and/features
pages of this starter by using the templateOtherPages.jsx
.
The best way to understand how it works is to read this article from the authors of DatoCMS and also to check the project files.
This opens the doors to different scenarios where for example, your content editors create pages, build the layout using a friendly UI completely without any developer intervention, just like they used to do with many Wordpress page builders.
You most like won't need to but if you want to access the pageLocale
, you can call this hook in any component file used in any of your templates:
import { usePageLocale } from '/src/hooks/usePageLocale';
And use it:
const { pageLocale } = usePageLocale();
console.log(pageLocale); // Logs "es-ES"
⚠️ Do not call this hook in your template files, call it in any component file and use it in your templates.
Same for the website languages:
import { useLocales } from '/src/hooks/useLocales';
const { locales, defaultLocale } = usedefaultLocale();
console.log(locales); // Logs ["en", "es", "it"]
console.log(defaultLocale); // Logs "en"
By default date strings coming from GraphQL queries:
query {
datoCmsBlogPost {
meta {
firstPublishedAt // <-- Always returns "2021-07-19T12:12:15.597+02:00"
}
}
}
Are formatted according to the page language and the following options:
const timeOptions = {
hour: '2-digit',
minute: '2-digit',
};
// Time: 23:30 ("it") or 11:30 PM ("en")
const dateOptions = {
year: 'numeric',
month: 'short',
day: 'numeric',
};
// Date: 23 Nov 2020 ("it") or Nov 23, 2020 ("en")
Options can be edited in src/functions/formatDateTime.js
.
There are two functions in the above mentioned file:
formatDate(dateString, pageLocale)
- Returns Jul 17, 2021
or 17 lug 2021
according to the page locale
formatDateTime(dateString, pageLocale)
- Returns Feb 10, 2022, 12:20 PM
or 10 feb 2022, 12:21
To quickly format the date coming from any DatoCMS / GraphQL field, import the hook:
import { useFormattedDate } from '../hooks/useFormattedDate';
And use it:
datoCmsBlogPost(originalId: { eq: $id }, locale: { eq: $locale }) {
meta {
updatedAt
}
...
const { formattedDate } = useFormattedDate(data.datoCmsBlogPost.meta.updatedAt);
<time>{formattedDate}</time>
If you want to increase the number of categories displayed by the dropdown, you can edit the file src/components/Layout/Header/Full
and increase the end
parameter of the Array.prototype.slice()
of the following variable:
const categoryNodesMatch = categoryNodes
.filter(({ locale }) => locale === pageLocale)
.slice(0, 6); // <-- Increase it
Each template must be wrapped in the component named <PageWrapper />
which accepts also 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 the fields return undefined or you don't set the props at all, global SEO fields will be rendered.
const HomePageTemplate = ({ data, pageContext }) => (
<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
}
}
...
This error will appear whenever the Navigator component is trying to render a link to a record which has never been generated as page by gatsby-node.js for that locale.
If the error for example appears in: localhost:8000/es/guia
, there's a problem with Navigator component inside that page in Spanish.
This can happen for many different reasons:
- You are passing the
id
field and notoriginalId
(with the aliasid
). - You forgot to export the
originalId
field with the aliasid
to the pageContext object when generating pages.
- You linked in a Structured Text field (on DatoCMS) a post/category which has been marked as non-translatable.
- You linked in a Structured Text a post whose category has been marked as non-translatable.
- You passed to the
renderLinkToRecord
StructuredText component prop theoriginalId
of the StructuredText field itself.
Check the StructuredText field content on DatoCMS for that locale and make sure of it.
Also, always filter any post or category query, by excluding any record marked as non-translatable:
query {
allDatoCmsCategory(filter: { noTranslate: { ne: true } }) {
categoryNodes: nodes {
id: originalId
}
}
}
Cannot query field "datoCmsContactPage" on type ...
You can encounter this error both during build time and development.
What happened here is that you removed a field or content model on DatoCMS and forgot to remove any query or variable referring to it in the project files. Please check this section on how to fix this issue.
If you encounter any different issue or you have any kind of request, please open an issue.