Inline editing refers to the ability to edit content directly on your site's page, as opposed to editing within the CMS sidebar.
The inlineJsonForm
higher-order component (HOC) let's us register inline forms. It can be used with both functional and class components.
The following example demostrates the first steps to adding inline editing to a component in Next.js:
Example: /pages/index.js
/*
** 1. Import the `inlineJsonForm` HOC
*/
import { inlineJsonForm } from 'next-tinacms-json'
import ReactMarkdown from 'react-markdown'
import Layout from '../components/Layout'
function Index({ jsonFile }) {
const data = jsonFile.data
return (
<Layout>
<section>
<ReactMarkdown>{data.body}</ReactMarkdown>
</section>
</Layout>
)
}
const formOptions = {
label: 'Index Page',
fields: [
//...
],
}
/*
** 2. Wrap your component with `inlineJsonForm`,
** pass in optional form field config object
** and declare a new variable to hold the
** returned component
*/
const EditableIndex = inlineJsonForm(Index, formOptions)
/*
** 3. Export the 'editable' version of the
** original component
*/
export default EditableIndex
/*
** 4. Call `getInitialProps` on the 'editable' component
*/
EditableIndex.getInitialProps = async function() {
const indexData = await import(`../data/index.json`)
return {
/*
** 5. Restructure your return data so the necessary
** Tina data is contained in an object titled
** `jsonFile`
*/
jsonFile: {
fileRelativePath: `data/index.json`,
data: indexData.default,
},
}
}
inlineJsonForm
expects the return data from getInitialProps
to match the following interface:
// A datastructure representing a JSON file stored in Git
interface JsonFile<T = any> {
fileRelativePath: string
data: T
}
Tip: It's important that the return object for data that will be edited by Tina is named
jsonFile
.
At this point the the inline form should be registered with the cms. In the next step, you'll need to add fields into your layout using the TinaField
component. The TinaField
component should wrap the HTML that outputs the contents of the field it edits. When editing mode is activated, the content will become editable.
In the following example, we wrap the section that renders the Markdown content in a TinaField
that uses the Wysiwyg
component. The Wysiwyg
component only renders when editing mode is triggered. This provides a field interface for editing on the page. When editing mode is off, the child component will render as normal.
import { inlineJsonForm } from 'next-tinacms-json'
import ReactMarkdown from 'react-markdown'
// 1. Import `TinaField`, along with any inline fields
import { Wysiwyg } from '@tinacms/fields'
import { TinaField } from '@tinacms/form-builder'
function Index({ jsonFile }) {
const data = jsonFile.data
return (
<Layout>
<section>
{/*
*** 2. Wrap the component that renders
*** the editable data with `TinaField`
*/}
<TinaField name="body" Component={Wysiwyg}>
<ReactMarkdown>{data.body}</ReactMarkdown>
</TinaField>
</section>
</Layout>
)
}
TinaField
is a higher-order component that accepts similar props as other fields.
interface TinaFieldsProps {
name: string
type?: string
Component: any
children: any
}
name
: The key for the value we want to edit from the content source.type
: Optional — Specifies a field plugin type.Component
: Either a React component that renders the field or a string containing the ID of a built-in field plugin.children
: The child component or element that renders the editable content.The last step to enable inline editing is to create a way for the editor to toggle editing states.
When your component is processed through inlineJsonForm
, it receives properties to facilitate this: isEditing
and setIsEditing
. You can create a button to handle toggling states and add it to your component.
/*
** 1. Destructure the editing state props
*/
function Index({ jsonFile, setIsEditing, isEditing }) {
const data = jsonFile.data
return (
<Layout>
<section>
<TinaField name="body" Component={Wysiwyg}>
<ReactMarkdown>{data.body}</ReactMarkdown>
</TinaField>
{/*
*** 2. Add a button that toggles
*** editing states
*/}
<button onClick={() => setIsEditing(p => !p)}>
{isEditing ? 'Preview' : 'Edit'}
</button>
</section>
</Layout>
)
}