This feature is available on Beefree SDK Core plan and above. On the Essentials plan? Upgrade a dev application to Core, free of charge, to try this and other premium features.
In this tutorial, you will learn how to implement Saved Rows in an application that has embedded Beefree SDK.
Also, see the sample code!
This feature is available on Beefree SDK Core plan and above. If you're on the Essentials plan, upgrade a development application for free to try this and other Core-level features.
Saved Rows allows users to select a row in a message and save it for later use. More specifically, it allows users to submit a request to the host application to save a piece of content and turn it into a reusable element. The host application, using the Custom Rows feature, can feed these saved elements back to the builder as rows that can be dragged into other messages.
When the feature is enabled, a new Save icon is added to the action icons when a row is selected:
The same action is also available in the row properties panel when a row is selected:
By clicking on this icon, users trigger a request to the host application to store the row’s JSON document, which includes:
row structure and settings;
contents and their settings;
all style settings.
It is entirely up to the host application:
where to store the JSON documents that describe these saved rows;
if and how to display them to users of the application;
whether to allow users to edit them individually
when and how to feed them back to the builder, using the Custom Rows feature.
Saved Rows – as most Beefree SDK features – is made available to users in an off state and must be activated in the Beefree SDK Console.
To do so:
Login into the Beefree SDK Console.
Click Details next to the application you want to configure.
Click the view more link under the Application configuration heading.
Toggle Enable saving rows on and click the Save button to save the new setting.
Once the feature has been turned on at the global level, in Beefree SDK Console, you may want to disable Saved Rows on a per-user basis. This can be accomplished via the client-side configuration document that you feed to an application when initializing the builder for a certain user.
Why? Because you may decide to make the feature available to different users of your application:
depending on the subscription plan that they are on (you could push users to a higher plan based on the ability to save a row for later);
depending on the purchase of an optional feature (same);
to allow “beta” users to see it while keeping it hidden from the rest of your users;
etc.
Here’s how to do so:
Enable Saved Rows in the Beefree SDK Console. as mentioned above.
Add the configuration parameter saveRows to the beeConfig document:
Set it to false for all users that cannot saved rows.
Here is a simple example:
How does Saved Rows work from the end-user point of view? It’s in part based on the changes to the builder mentioned above, and in part affected by how you decide to implement the feature within your application.
Let’s review the various steps in the workflow to better understand what we mean.
Saving a row:
Activate the feature as described above.
Load a Beefree SDK template.
Select the row you want to save.
Click on the Save icon.
Your application will need to open some sort of a dialog that allows for some user input.
Users will enter various metadata, such as the name of the row, a category, tags, etc.
The user closes the dialog
Using a previously saved row:
Users click on the Rows tab.
Users select a category of rows from the Rows tab’s drop-down menu. For example:
A user created a “Footers” category when saving a row
You saved the row and that category name
You fed an array of custom rows called “Footers” back to the builder
The same user finds “Footers” in the Rows drop-down menu.
The user drags the row into the editing stage.
Here is a visual example using our demo application:
When the saved row action is triggered by the user, the builder starts the following sequence:
Metadata Content dialog Used to collect data from the host application and add it to the row object. Metadata helps your application to identify a row, overwrite a previously saved version, etc.
Saved Rows Callback. Function that returns the row to the host application.
The following describes the recommended workflow to implement saved rows in a host SaaS application.
Enable Saved Rows in the Beefree SDK Console as described above.
Load a Beefree SDK template.
Select the row you want to save and make note of the new save icon.
Click the save icon to trigger a Metadata Content Dialog. To successfully handle this step, you must complete these tasks:
Add a Metadata Content Dialog object to your beeConfig. This configures your handler.
Implement the handler method to open a dialog (e.g., modal window) to collect any metadata you wish your users to input when saving a row.
The dialog should contain a form and complete the following specs:
Save the row returned in the Metadata Content Dialog’s args object.
Collect metadata from the end-user, such as row name.
Merge the metadata with the row, so it can be immediately returned to the application.
Return a metadata object to the application so the stage can immediately use the data.
The application will update the selected row on the stage with the returned metadata.
The application will trigger the onSaveRow callback with the following details:
JSON of the selected row
HTML preview of the selected row
Page Partial of the selected row contained in a page. Use this JSON document to allow users to edit a saved row independently of any message or landing page that might use it.
The application will refresh the Rows panel to reload the selected rows data feed.
Host app will listen for onSaveRows callback and update the previously saved records with the HTML preview.
To display saved rows in the Rows tab, add them to the list of rows available to users by leveraging the Custom Rows feature.
The rows are organized in lists that are displayed based on your rows configuration. Use the metadata submitted by your users to categorize them, creating multiple lists of rows: this can significantly improve the user experience.
Here is an example of a rows configuration that displays saved rows organized by category:
In this example, the Rows tab will show:
Empty rows
Default rows
Headers
Footers
Product grids
Main article
… retrieving the arrays of JSON documents for custom rows (externalContentURLs) from the URLs specified.
These custom rows names (Headers, Footers, Product grids, etc.) could be the result of a “Category” metadata entered by the user at the time the row was saved. The input could be the result of:
The user writing a new category name for the selected row.
The user selecting from a list of existing categories, previously created by the user, or set up by you.
Here is another example that shows saved rows organized in the Rows tab based on the campaign type:
The maxRowsDisplayed
parameter enables you to define the number of rows displayed under each user-created category in the application's sidebar, without affecting the "Empty" and "Default" categories. It directly influences the number of saved rows an end user sees when they click on a category in the sidebar.
You can set the maxRowsDisplayed
parameter in the rowsConfiguration
object in the Beefree SDK Configuration as follows:
Accessing, and organizing saved rows is now easier than ever with Saved Rows Management. With this feature, we’ve introduced a new action in the list of saved rows that your application can intercept to handle changes in this list itself. This means you can now delete, rename, or re-organize your saved rows, right inside the builder.
Implementing this new feature requires some development effort from the host application.
Here is what you need to know for each action.
In the section below you can learn how to configure the Saved Rows Management categories, and allow users to perform such actions straight from the builder.
To get started, you will need to create a content dialog in your application configuration parameters. The content dialog method should be named onDeleteRow
and be nested under the contentDialog
object, as follows:
Following that, amend your rowsConfiguration
object with the additional parameters:
The handle
parameter to utilize in your onDeleteRow
handler from the previous step
The optional behaviors
parameter to set management permissions
Here’s an example:
When the onDeleteRow
method is called, utilize the 3rd parameter to obtain an argument containing the handle value of the row being requested, as well as the row metadata. Use the handle and the row’s metadata to determine which row should be deleted.
Finally, we can call the resolve
method, passing the value true
if you want to refresh the rows, or false
if you want to keep the side panel’s current listing.
To get started, much like with deleting rows, you will need to create a content dialog in your application configuration parameters. The content dialog method should be named onEditRow
and be nested under the contentDialog
object, as follows:
Following that, amend your rowsConfiguration
object with the additional parameters:
The handle
parameter to utilize in your onEditRow
handler from the previous step
The optional behaviors
parameter to set management permissions
Here’s an example:
When the onEditRow
method is called, utilize the 3rd parameter to obtain an argument containing the handle value of the row being requested, as well as the row metadata. Use the handle and the row’s metadata to determine which row should be edited.
Finally, we can call the resolve
method, passing the value true
if you want to refresh the rows, or false
if you want to keep the side panel’s current listing.
Saved Rows Management also provides errors and warnings for your application, so you can handle all cases gracefully.
Sample warning:
You can call the reject
method, passing the message you want to display.
Saved Rows Management also comes with the ability to load any external rows via an instance method instead of an external URL. In addition, since you can now access rows through your application, you don’t need to perform authentication.
To start, define a hook in your application configuration. The hook method should be named getRows
and will be nested under the hooks
object, as follows:
Following that, amend your rowsConfiguration
object with the additional parameters:
The handle
parameter to utilize in your getRows
handler from the previous step
The isLocal
parameter to let the application know to use the hook handler
When the getRows method is invoked, utilize the 3rd parameter to obtain an argument containing the handle value of the row being requested. Use the handle to determine which set of rows should be returned.
Finally, we can call the resolve method, passing in an array of savedRows.
If you are using a React application, be sure to pass a new rows array and not a reference to a row state. Otherwise, the rows state may be “stale” and won’t update in the side panel.
The following is the basic structure of the row’s JSON schema. Simply put, the schema is the structure of your saved rows data feed.
NOTE: The row schema is complex and we do not recommend creating rows programmatically. Therefore, there is no schema reference of the row itself. However, you can add your own parameters to the row’s metadata or use our Simplified Row Schema to generate them programmatically from existing content.
The metadata section of the rows schema allows you to keep track of row-specific information.
A string of plain text that identifies the row.
Displayed in the row card when the row is shown in the Rows panel.
Used for text searches within the Rows panel
category A category can be useful for organizing your feeds on the Rows tab.
id A handle that identifies the row in the host application’s data storage.
idParent Useful to track rows that were saved from previously saved rows. Keeping track of where a row came from allows you to implement additional editing features.
dateCreated The date the row was created: useful for filtering/sorting rows for content management purposes in your application. It can also help with technical support tasks.
dateModified The date a saved row was updated: useful for filtering/sorting rows for content management purposes in your application. It can also help with technical support tasks.
userId To let your application decide whom can edit or saved rows.
tags Useful to create filters, management, search, and in general to organize the content in your application.
The metadata content dialog is triggered by the save icon in Beefree SDK. This step is required to provide Beefree SDK with information about the row, such as its name and/or id. The Metadata Content Dialog is added in the same manner as other Content Dialogs, such as Merge Tags. Please review the Content Dialog section for more details about how to use Beefree SDK’s Content Dialog feature.
An example Metadata Content Dialog configuration can be found below.
The metadata resolve function now accepts an options
object in which you can pass the property synced
to determine if the row needs to be saved and treated by the builder as synced.
When the Metadata Content Dialog is completed, the application triggers the Saved Rows callback. The callback returns the following details:
rowJSON JSON of the selected row.
rowHTML HTML preview of the selected row
pageJSON Page Partial of the selected row contained in a page (for editing a row as an independent piece of content).
With Edit Single Row mode you can offer an easy way for your users to edit saved rows individually, using a tailored UI built to modify the row structure, content, and style settings without worrying about messing up with the overall design of the email campaign, landing page, or pop-up.
Enabling a more modular approach to saved rows simplifies how users can design and act on content: updating small details in a saved row, saving it, then deploying it to existing templates becomes a matter of minutes. If you want to learn more about how to leverage Edit Single Row mode to safely modify a Saved Row, take a look at the dedicated technical documentation.
This feature is available on Beefree SDK and above. If you're on the Essentials plan, for free to try this and other Core-level features.
Synced Rows expands on the foundational capabilities of and , helping users manage rows more effectively. Using the merge-rows
and synced-rows
methods in the , app developers can create a seamless row management workflow. This ensures that when users update content in one row marked as “synced,” those updates are reflected across all connected designs.
Cross-design synchronization: Sync saved row contents across multiple linked designs.
Design consistency: Lock rows in linked designs to keep designs uniform.
Editing flexibility: Switch between synced and unsynced rows, making individual changes as needed.
Intuitive edit indicator: Look for the pencil icon at the top-right of synced rows, which provides access to editing options
Auto-updating contacts: Change a contact in one email, and it updates across multiple campaigns.
Effortless re-branding: Edit your logo once and watch it reflect across all designs.
Unified transactional footers: Update details like copyright or social links, and it automatically updates in all relevant email templates.
Row details: This includes structure, settings, and styles.
Content details: Settings and styles of individual content blocks.
Metadata: Any metadata tied to the saved row.
Before diving into Synced Rows, we recommend reviewing the following features:
When a row is saved with the synced
property, it becomes a “synced row.” To maintain consistency, synced rows cannot be edited within a design. Instead, they function as reference points, ensuring uniformity across all linked designs. The host app must load the row’s JSON using Single Row Edit Mode to edit synced rows. Any modifications to synced rows can be propagated to all linked designs with the help of the CSAPI’s merge-rows
and synced-rows
methods.
Unsynced saved rows, in contrast, allow for edits that don’t impact other designs. They’re ideal for making design-specific changes without influencing other designs that might share the same base row.
As previously mentioned, a synced row is a saved row designated synced
when saved. To set a row’s synced property, adjust the JSON response from the saveRow
Content Dialog.
You might need to modify the saveRow
handler from the Metadata Content Dialog step in your app’s Save Rows workflow.
If you need a refresher, check out:
Here’s a sample implementation for the Metadata Content Dialog, offering the synced row option:
The JSON returned to the builder includes the user’s input and selections from the UI. The configuration below shows the new synced row setting applied to the options argument of the resolve method.
Look for the pencil icon at the top-right of synced rows. Rows without this icon are standard saved rows. The icon provides a clear visual cue for quickly identifying and editing synced rows.
To edit a synced row, click the pencil icon. Editing options appear in the sidebar panel. Inside, you’ll find a CTA button and optional text.
The CTA button opens the editSyncedRow
Content Dialog, allowing the host application to interact with the end-user and receive their selection.
The host application has complete control over the content dialog and UX. However, the content dialog must always return a boolean value of true
or false
to trigger one of the following outcomes:
If true
, the row remains synced to disable content editing in the design before closing the dialog.
If false
, the row updates to enable editing and remove the synced property before closing the dialog.
For example, a content dialog might present users with two options:
Edit the row across all designs.
Unsync the row, turning it into a standard saved row.
The first option, to edit the row across all designs, allows users to make changes to the synced row that will be reflected in all designs that use it.
The second option, to unsync the row, converts the synced row into a standard saved row. This means that any changes made to the row will only affect the design in which it is being edited. This option is useful when the user needs to make specific changes to a single design without affecting other designs that may use the same saved row.
The user’s selection from the above example editSyncedRow
content dialog UI is returned to the builder as a boolean value. Below is an example of the editSyncedRow
configuration for the UI above. Note the boolean value false
in the resolve method unlocks the row:
The following animation shows this example of edit synced rows workflow in action. We’ll dive into this process in the following sections.
Suppose a user selects “Edit and update everywhere” from the content dialog. How does the host app ensure seamless editing and synchronization of the row?
Here’s a breakdown of the typical workflow the host app adopts:
User edits: Users generally hit ‘Save’ to confirm their edits once they modify the synchronized row. Simultaneously, the host app can proactively track these edits using the onChange
method.
Synchronization timing: The decision on when to synchronize changes across all designs rests with the host application. Given the potential need to propagate edits to multiple designs, holding off until the user indicates their wish to exit is customary.
Initiation of synchronization: The synchronization is initiated as soon as the user signifies their satisfaction with the edits, either through the ‘Save’ or ‘Save & Exit’ options.
Background syncing: Given the possible existence of numerous linked designs, a background process is usually set in motion to update all other templates.
In the fifth step of the sample workflow, the goal is to bring the user back to their ongoing design with the updated content. This is achieved using the CSAPI’s merge-rows
method.
The merge-rows
method functions as a sophisticated “find and replace.”
i.e., To synchronize content, the host app forwards a request comprising the outdated template, the newly edited row, and a succinct query detailing which row(s) the API should target for replacement. The “query” is a standards-based JSON Path query typically referencing a globally unique identifier that was added to the saved row during the Metadata Content Dialog step.
To update rows across all designs, keeping track of the templates where rows have been dropped is crucial. There are two principal methods to associate rows with templates:
Using the onChange
method
Upon adding a synced row into a design, the onChange
callback method supplies the row’s metadata. The metadata will contain the row’s guid
, which can be used to link the synced row to the guid
assigned to the template by the host app. Commonly, this is achieved by establishing an index record within the app’s database linking the template’s guid
with the row’s guid
.
Leverage the synced-rows
method
The specific objectives of the host application steer the choice between these methods. Whatever the choice, the primary focus is meticulously tracking the row across all linked templates, ensuring accurate and efficient updates.
This feature is available on Beefree SDK and above. If you're on the Essentials plan, for free to try this and other Core-level features.
Our builders offer , which provide both structure and content to create contents faster. With Edit Single Row mode you can offer an easier way for your users to modify a single row with a tailored UI built to edit the row structure, content, and style settings without worrying about messing up with the overall design of the email campaign, landing page, or pop-up.
Edit Single Row mode complements the as it allows a complete control over the content of individual rows (e.g. the footer that requires to be updated) without the need to intervene into a full template, this will help you in implementing a more effective way to manage libraries of Saved Rows with a streamlined design process.
When a builder application is initialized with this mode enabled the UI will show to the user only properties that pertain to editing a single row, therefore:
the options to insert custom rows, saved rows, or new default rows are disabled,
the Settings tab is unavailable, as those properties are specific to the entire document,
when a row is selected on the editing stage, the action to Delete, Duplicate, Comment, Save are not available.
The following describes the recommended workflow to implement the Save action in your host SaaS application when the Single Edit Row mode is enabled.
The user clicks on the save button
A contentDialog of type saveRow will be triggered.
In case your application doesn’t use the default Toolbar you will need to handle the row saving in a different way, following a couple of examples:
Beefree SDK offers Saved Rows. Learn more about what this feature is and what it offers in the following sections.
: Implement this feature first, as it establishes the foundation for a row management workflow.
& : Familiarizing yourself with the Single Row Edit Mode and CSAPI will enhance your understanding of key concepts later. However, they are not strictly necessary to start with Synced Rows.
A comprehensive reference of the editSyncedRow
Content Dialog settings, such as the CTA button label and optional text, can be found in our .
Initialization: The host app launches the Single Row Edit Mode in a new builder instance to enable editing of the synchronized row. Our documentation is available for reference for a deeper understanding.
Redirection & Synchronizing changes: After editing a row, the host app usually performs a synchronous . The user will then be redirected to their ongoing design, where they can see their edits reflected in the synced row.
For an example on how to use the merge-rows
method, .
For those rows incorporated into designs before the implementation of the onChange
tracking method, CSAPI’s synced-rows
method is available. Use the synced-rows
method get a list of all the synced rows inside a template with their corresponding rowIdentifier
values. To learn more about how to use this endpoint, visit our Content Services API Reference on .
In case your application uses the default , you can leverage the save button to trigger the sequence of action to correctly save the row, the workflow is the same as the one documented in , in short :
Calling the . It will trigger the on event with two arguments, one of them is the full message JSON that can be saved as a Saved Row (it’s the same JSON returned by the event).
Listening to the event. It will receive the updated full message JSON which again can be saved as a Saved Row.
An effective way to update saved rows across multiple templates is by implementing the save action in combination with the , to handle a row update across multiple .