Working with Backends


You may have noticed that content changes don't persist on refresh. Every time the page loads, the data populates with the form's initial values. In order for the CMS to be useful, it needs to track and persist data changes with some sort of backend.

Loading Content from an external API

For this example, we will use a fake API to fetch content and post changes. We will use the loadInitialValues function to fetch content.

src/App.js

//..

-const pageData = {
- title: 'Tina is not a CMS',
- body: 'It is a toolkit for creating a custom CMS.',
-}

function PageContent() {
  const formConfig = {
    id: 'tina-tutorial-index',
    label: 'Edit Page',
    fields: [
      //...
    ],
-   initialValues: pageData,
+   loadInitialValues() {
+     return fetch(
+       'https://jsonplaceholder.typicode.com/posts/1'
+     ).then((response) => response.json());
+   },
    onSubmit: async () => {
      window.alert('Saved!');
    },
  }

  //...
}

//...

Notice how we removed initialValues in favor of loadInitialValues, which fetches data asynchronously on form creation. Learn more about handling loading states when loadInitialValues is resolving.

You can use initialValues when your data has already been fetched or defined before your components mount. Typically you would use this if you prefer to fetch your initial data server-side, as we do in our Next.js + GitHub example

Saving Content

Next we'll adjust the onSubmit function. When the editor clicks the 'Save' button in the sidebar, onSubmit will be called, sending the updated data back to the API:

src/App.js

function PageContent() {
  const formConfig = {
    id: 'tina-tutorial-index',
    label: 'Edit Page',
    fields: [
      //...
    ],
    loadInitialValues() {
      return fetch(
        'https://jsonplaceholder.typicode.com/posts/1'
      ).then(response => response.json())
    },
-   onSubmit: async () => {
-     window.alert('Saved!');
-   },
+   onSubmit(formData) {
+    return fetch('https://jsonplaceholder.typicode.com/posts/1', {
+       method: 'PUT',
+       body: JSON.stringify({
+         id: 1,
+         title: formData.title,
+         body: formData.body,
+         userId: 1,
+       }),
+       headers: {
+         'Content-type': 'application/json; charset=UTF-8',
+       },
+     })
+       .then(response => response.json())
+       .then(data => console.log(data))
+       .catch(e => console.error(e))
+   },
  }

  //...
}

Note that this onSubmit function won't actually save those new values on the server — it is a fake API after all. But the response will act as if it did. This example just puts the response in the console to show what was returned.

Adding Alerts

While our form is functional in terms of retrieving and saving content, the editing experience could be improved by additional feedback. What if the form failed to save? Currently, the editor would need to check the console to see that there was an error.

CMS Alerts are useful for displaying quick, short feedback to the editor. Let's add a few to improve the feedback for saving content.

The Steps

  1. Access the CMS object through the useCMS hook.
  2. Use the info,success, & error methods to trigger alerts with a custom messages.

src/App.js

//...

function PageContent() {
+ const cms = useCMS();
  const formConfig = {
    id: 'tina-tutorial-index',
    label: 'Edit Page',
    fields: [
      //...
    ],
    loadInitialValues() {
      //...
    },
    onSubmit(formData) {
+     cms.alerts.info('Saving Content...')
      fetch('https://jsonplaceholder.typicode.com/posts/1', {
        //...
      })
        .then((response) => response.json())
-       .then(data => console.log(data))
+       .then((data) => {
+         cms.alerts.success('Saved Content!');
+         console.log(data);
+       })
-       .catch(e => console.error(e))
+       .catch((e) => {
+         cms.alerts.error('Error Saving Content');
+         console.error(e);
+       });
    },
  };

  //...
}

//...

tinacms-success-alert

Tip: Dispatching alerts can be powerful in combination with CMS Events.

Other backends

Tina is fairly unopinionated about how it receives data to edit, or where that data is stored. The backend you choose depends on your project and the React meta-framework you may be using. Currently, there are numerous packages to support Git & GitHub workflows, but Tina is designed to potentially work with any data source. We have also made working prototypes to source data from Strapi and Contentful (not yet documented).

Please refer to the guides for in-depth information on setting up various backends. Also, refer to the forum to read about other developers' unique configurations. As always, reach out to the Tina Team on the forum if you have an integration or backend idea and would like guidance or feedback on how to get started.

Final Notes

Tina is a toolkit to build your own custom CMS. It’s not a one-size-fits-all approach like a conventional CMS. We think the main aspects to consider when building a CMS are: constructing the editing interface, storing and managing data, and then integrating with various frameworks.

We’d like to provide developers with control and flexibility in all these aspects related to building a custom CMS. Right now, we’re building Tina from the ground up, trying to make a solid foundation with React and Git-oriented workflows, but that’s just the beginning.

Avenues to keep learning:

Follow Tina on Twitter 🦙! If you're stoked on this project, please give us a 🌟 on the GitHub repository.

Stay up to date with the latest developments via Release Notes or reach out on the forum with ideas or questions. Interested in contributing? Get started via the project README.