NextJS/JSS Edit Frames Before JSS v21.1.0

Overview
I wanted to implement edit Frames on a NextJS/TS/JSS v20.1.3 site. I was disappointed to find out that official support for edit frames was only added in v21.1.0, so I went out looking for other options.
Why Edit Frames?
Edit frames are a useful tool for content authors to edit arbitrary fields on arbitrary items via arbitrarily placed clickable elements on a page. One example would be if you wanted a way for a user to edit a background image on a section. You might render an absolutely positioned clickable icon that would allow the background image field to be edited. The possibilities are endless.
The Implementation
I was inspired by this SSE post about possible solutions: https://sitecore.stackexchange.com/questions/24307/edit-frame-with-sitecore-jss.
One of the answers points to a custom implementation of edit frames that someone wrote ~3.5 years before Sitecore added official support for it 👀: https://github.com/Sitecore/jss/pull/290. I refactored / modernized the code to work with strict TypeScript and the latest React patterns.
The Code
import React from 'react';import { useSitecoreContext } from '@sitecore-jss/sitecore-jss-nextjs';
interface FieldEditFrameProps { itemId?: string; fields?: string[]; header?: string; icon?: string; disabledIcon?: string; tooltip?: string; command?: string; commandDisplayName?: string; databaseName?: string; language?: string; hideIfNotPageEditing?: boolean;}
const FieldEditFrame: React.FC<FieldEditFrameProps> = ({ itemId = '', fields = [], header = 'Edit Fields', icon = '/temp/iconcache/people/16x16/cubes_blue.png', disabledIcon = '/temp/cubes_blue_disabled16x16.png', tooltip, command = '{70C4EED5-D4CD-4D7D-9763-80C42504F5E7}', commandDisplayName, databaseName, language, hideIfNotPageEditing, children,}) => { const { sitecoreContext } = useSitecoreContext(); const { pageEditing, language: sitecoreLanguage, route } = sitecoreContext; const shouldRender = pageEditing && itemId && fields && fields.length;
if (!shouldRender && hideIfNotPageEditing) { return null; }
if (!itemId || !fields) { return null; }
// TODO: handle route undefined const contextItemUri = `sitecore://${databaseName || route?.databaseName}/${itemId}?lang=${ language || sitecoreLanguage }`;
const clickCommandAction = `webedit:fieldeditor(command=${command},fields=${fields.join( '|' )},id=${itemId})`;
const clickCommand = { click: `javascript:Sitecore.PageModes.PageEditor.postRequest('${clickCommandAction}',null,false)`, header: header, icon: icon, disabledIcon: disabledIcon, isDivider: false, tooltip: tooltip || `Edit the following fields: ${fields.join(', ')}`, type: null, }; const commandData = { commands: [clickCommand], contextItemUri: contextItemUri, custom: {}, displayName: commandDisplayName || 'Edit Properties', expandedDisplayName: '', };
const divAttrs = { sc_item: contextItemUri, 'sc-part-of': 'editframe', };
return ( <div className="scLooseFrameZone scEnabledChrome" {...divAttrs}> <span className="scChromeData">{JSON.stringify(commandData)}</span> {children} </div> );};
export default FieldEditFrame;Example Implementation
import FieldEditFrame from './../../utils/FieldEditFrame';
// other code
return ( <> <FieldEditFrame // of course, its better not to hardcode these values itemId="{FEA43559-D12F-47C9-911C-8170B7FAEFA4}" fields={['Alert Label']} > <h2>Goo Goo Ga Ga</h2> </FieldEditFrame> </>);Now the element is clickable:

And clicking the blocks icon opens the field editor window:

Further Steps
Be sure to do some extra testing on your local:
- How does it behave during hot reloads?
- Any console errors?
- Does it work with multiple edit frames on the same page?
Build or beg,
Marcel





