This starter can be used for sites that source their data primarily from Silverstripe CMS.
$ gatsby new my-ss-project https://github.com/unclecheese/gatsby-starter-silverstripe
You will need to install the following package on your Silverstripe site to expose your data to Gatsby:
$ composer require silverstripe/silverstripe-gatsby
$ composer require sminnee/silverstripe-apikey
Once that is installed, ensure the __gatsby/graphql
endpoing is working on your Silverstripe site.
Generate an API key with ADMIN permissions to grab the site tree
Now, edit the gatsby-config.js
file, and add the host of your Silverstripe backend to the options.host
node in the plugin config.
{
resolve: `gatsby-source-silverstripe`,
options: {
host: `http://my-silverstripe-cms.local`,
api_key: `SECRETAPIKEYSTRING`
}
},
Now, run gatsby develop
. When the build is complete, you should have your entire site built out, with primary and secondary navigation,
in a primitive template.
This starter provides several features that aim to maintain a high level of parity between your Gatsby project and Silverstripe in order to ease migration pain and flatten the learning curve.
Any dataobject exposed by your Silverstripe backend that has a Link()
method will get its own page in your Gatsby site,
whether it's a SiteTree or DataObject.
By default, a templates/Layout/Page.js
is provided, which should resolve all your SiteTree records.
Template selection works the same way as Silverstripe.
Template inventory:
src/
templates/
Page.js
Layout/
NewsPage.js
Page.js
NewsPage extends Page
=>src/templates/Layout/NewsPage.js
(match)ContactPage extends Page
=>src/templates/Layout/Page.js
(resolves to parent class)StaffMember extends DataObject
=> NOT FOUND (Please create aLayout/StaffMember.js
file)
The starter ships with some basic components to help with common template needs. You're free to customise these as you see fit.
<MainNav />
<Breadcrumbs />
<SEOTags />
You can query forms as data, as they are provided by silverstripe/silverstripe-gatsby
using the silverstripe-graphql-forms module. You will need to expose these forms to your GraphQL API on your Silverstripe site.
UncleCheese\GraphQLForms\FormQueryCreator:
registered_forms:
## Expose a form that is generated by a method on a controller
ContactForm: 'MyProject\Pages\ContactPageController.ContactForm'
## Expose a UserDefinedForm by ID
MyUserDefinedForm: 45
Then, you can extract the form data using helpers, and render the form out with a library like formik.
import React from 'react';
import { extractFormData } from 'silverstripe-gatsby-helpers';
const MyFormPage = ({ data: { silverStripeDataObject, silverStripeForm } }) => {
const { title, content } = silverStripeDataObject.SilverStripeSiteTree;
const {
initialValues,
fields,
actions,
validator,
attributes
} = extractFormData(silverStripeForm);
// Render form
};
See full example below.
Coming soon.
import React from 'react';
import classnames from 'classnames';
import { getMenu } from 'silverstripe-gatsby-helpers';
import { Link } from 'gatsby';
const MainNav = () => {
const menuItems = getMenu(1);
return (
<nav className="mainNav">
<ul>
{menuItems.map(item => (
<li key={item.id} className={classnames({
current: item.isCurrent,
section: item.isSection,
})}>
<Link to={item.link}>
{item.SilverStripeSiteTree.menuTitle}
</Link>
</li>
))}
</ul>
</nav>
);
};
import React from 'react';
import { getChildren, isLevel } from 'silverstripe-gatsby-helpers';
const children = getChildren();
const isLevel2 = isLevel(2);
const hasSubnav = isLevel(2) || !!children.length;
const navItems = isLevel2 ? getMenu(2) : children;
const MyPage = () => (
<div>
{hasSubnav &&
<div className="sidebar">
<h2>In this section</h2>
<ul>
{navItems.map(child => (
<li key={child.id} className={classnames({
current: child.isCurrent,
})}>
<Link to={child.link}>{child.SilverStripeSiteTree.title}</Link>
</li>
))}
</ul>
</div>
}
</div>
);
import React from 'react';
import { graphql } from 'gatsby';
import { Formik, Form, Field } from 'formik';
import { extractFormData, SSFieldHolder } from 'silverstripe-gatsby-helpers';
const renderField = fieldNode => {
const { Component } = fieldNode;
return (
<SSFieldHolder key={fieldNode.formFieldID} fieldNode={fieldNode}>
<Field component={Component} />
</SSFieldHolder>
)
};
const MyFormPage = ({ data: { silverStripeForm } }) => {
const {
initialValues,
fields,
actions,
validator,
attributes
} = extractFormData(silverStripeForm);
return (
<div>
<h2>Form</h2>
<Formik
initialValues={initialValues}
validate={validator}
onSubmit={(values, { setSubmitting }) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
setSubmitting(false);
}, 400);
}}
>
{formik => (
<Form {...attributes}>
<fieldset>
{fields.map(renderField)}
{actions.length > 0 &&
<div className="btn-toolbar">
{actions.map(action => (
<button key={action.formFieldID} type="submit" name={action.name} id={action.formFieldID}>
{action.title}
</button>
))}
</div>
}
</fieldset>
</Form>
)}
</Formik>
</div>
);
};
export const pageQuery = graphql`
query($link: String!) {
silverStripeForm(formName: { eq: "ContactForm" }) {
attributes {
name
value
}
formFields {
...SilverStripeFormFieldsFields
attributes {
name
value
}
}
formActions {
...SilverStripeFormFieldsFields
attributes {
name
value
}
}
}
}
`;
export default MyFormPage;
For more information, see the silverstripe-gatsby-helpers package.
This starter adds some hooks to your gatsby-node.js
file to inject global state that is used by most of the helper functions
in silverstripe-gatsby-helpers
. (See: SiteTreeProvider
). Due to the nature of Gatsby's useStaticQuery
,
this code has to be colocated with your project. It is not recommended that you edit it.