Hi! 😀
In this article, I’m gonna share something very cool which was not possible to do declaratively before Spring ’21.
With Spring ’21 Salesforce introduced a feature that now allows us to send Rich Text Emails via flows. And with the help of this amazing feature, I’ll show you how we can embed table(s) containing related records data in our emails.
For some context, let’s take a simple use case.
Use case
We want to send a list of open cases, in tabular format, related to an Account to the Account owner.
Let’s Roll
For reference, this is how the final flow should look like:

Step 1: Create a variable, parentAccount as follows.

Step 2: Get all the open child cases using Get Records element.

Step 3: Create a variable, table as follows:

Step 4: Create a variable, tableColumns as follows:
<tr>
<th style="border: 1px solid black;">CaseNumber</th>
<th style="border: 1px solid black;">Subject</th>
<th style="border: 1px solid black;">Status</th>
<th style="border: 1px solid black;">Priority</th>
</tr>

Step 5: Use an Assignment element to ‘add’ tableColumns to table variable.

Step 6: Add a Loop element to iterate over all the open child cases.

Step 7: Create a formula, tableRow as follows:
'<tr><td style="border: 1px solid black;">' + {!OpenChildCase.CaseNumber} +
'</th><td style="border: 1px solid black;">' + {!OpenChildCase.Subject} +
'</th><td style="border: 1px solid black;">' + TEXT({!OpenChildCase.Status}) +
'</th><td style="border: 1px solid black;">' + TEXT({!OpenChildCase.Priority}) +
'</th></tr>'

Step 8: Use an Assignment element to ‘add’ tableRow to table variable.

Step 9: Use an Assignment element to ‘add’ ” </table>” to table variable.

Step 10: Create a Text Template variable, emailBody as follows:

Step 11: Send the email using Send Email action as follows.

And we’re all set.
How it’d look like?

To save you some work, I packaged the flow which you can install from here: https://login.salesforce.com/packaging/installPackage.apexp?p0=04t6F000001ZLuu
What more can you do?
- You can get as fancy as you want by adding inline styling(CSS) to the table header and rows.
- You can also use subflows to make it reusable to some extent.
I hope you find this useful! I’ll catch you in the next one! ✌
Thank you for being an awesome reader! Subscribe to ForcePanda to receive all the latest updates straight to your inbox.
Do you have a sample image of what this output would look like?
LikeLike
Yes, let me add it right way!
Thanks for the suggestion! 😀
LikeLike
This is awesome! Thank you for this! How would we make each case within the table in the email clickable for the recipient to navigate to?
LikeLike
In the tableRow formula, you’d need to do something like this:
‘‘ + {!Case.CaseNumber} + ‘
LikeLike
Did you ever figure this out – I tried today but kept getting a HL_ENCODED error
LikeLike
Pingback: Send Emails with Tables in Flow – NPSP Example with Payments – Sunshine and Other Unhandled Exceptions
Pingback: Track Field History on User Object Using Before-Save Flow – The SFDC Post
I would like to add images to the table.
However, I don’t want the images to be showing up as links. The field on Salesforce is a url field.
They should be embedded within the table so that they are visible on the email. The field on Salesforce is a url field. How can I embed images into the table using the url fields?
LikeLike
I think you should be able to use the ‘img’ tag to put the image in the table. You can read more about it here:
https://www.w3schools.com/tags/tag_img.asp
LikeLike
I just tested this solution. It works perfectly. Thank you ForcePanda.
LikeLike
Most welcome! 🙂
LikeLike
This is awesome! Thank you! I just tested this solution, everything worked perfectly except the Piclist fields are not allowed to be added to the table and any look-up fields will return the ID instead of the actual name. Please advise!
LikeLike
For lookup, you will have to do something like this. Say you want contact’s account name:
{!record_variable.Account.Name}
For picklist, use formula variable like this:
TEXT({!record_variable.Picklist})
LikeLiked by 1 person
Works perfectly. Thanks.
LikeLike
Awesome!
LikeLike
Thanks for the steps. If you have sometime please make a video. Much appreciated, awesome work.
LikeLike
Hi Stel,
I’m not sure when I’ll be able to make a video for this. But I’ve added a link to the package that you can install directly to save you some work.
Hope that helps. 🙂
LikeLike
Thank you for the link. It shows in the package installed “FlowEmailWithTableDemo” but when I got to flows is not there “FlowEmailWithTableDemo” . Why you think that is?
LikeLike
Hi,
I think the name of the flow is Demo Flow
LikeLike
Pingback: Send Tables in Email in Flow – NPSP Example #2 Memorial Gifts – Sunshine and Other Unhandled Exceptions
Hi ForcePanda,
Thank you for the excellent tutorial – very helpful for practicing Flows and improving my understanding.
For getting records, should we store all records? Otherwise, “OpenChildCases” does not show up as a potential record collection variable in the “OpenChildCase” loop. I bring this up only because “Only the first record” is the default option for Get Records.
Thanks for your help!
LikeLike
Yes, you want to store all the child records in a collection type variable so that you can iterate over it using the Loop element.
LikeLike
This is a gem – thanks for sharing! Replaces the need for visualforce email templates. Is it possible to hide the table if no data exists?
LikeLike
Yeah, you’d apply some CSS magic to hide the table.
LikeLike
For the more declarative among us, this could also be done by checking the collection for is null in a decision and using a text template without the table!
LikeLike
This is great! Thank you!!!
LikeLike
love this – works perfectly, thank you! also big fan of your blog in general 🙂
LikeLike
Thank you! 🙂
LikeLike
This is so cool! Is there a way that I could have the Flow just generate the table, and insert the table from a Salesforce Lightning Email Template? That’s actually our ideal use case – I’d love to create an email template that lets me pop the table into it rather than clicking a button to run a screen flow on one account at a time. Thank you again for creating this, teaching all of us, and packaging it up for our own tweaking!
LikeLike
Aah! I figured it out! Instead of sending an email, I can have the flow update a Rich Text field that can then be merged into the Lightning Email Template. So cool. I’m going to be the hero when I tell my staff that the thing we’ve been wanting to do for like 8 years is finally possible. Thank you so much for this post!
LikeLike
Glad you found it useful! 🙂
LikeLike
I find that each iteration of the loop overwrites the first data row in the table with the data for the current iteration. Any idea why?
LikeLike
Wouldn’t be possible without taking look at the flow. See, if you can compare your flow to the flow I’ve packaged in the post.
LikeLike
Hi,
I have tried to create the similar flow for some custom object (master detail relationship) but record-triggered instead of screen one. I am getting the email but with the {!table} placeholder instead of proper table. I have checked and it seems that I have replicated every step correctly. Do you know what could be the issue?.
Regards,
Mateusz
LikeLike
Hard to tell without taking a finer look at it. You’d post it on the QnA forums if you’d like.
LikeLike
Thanks so much for this! It was super helpful, and am now using it in multiple places.
I’ve tried putting in some more inline CSS into this, but struggling with it – I’m not sure if there are only certain CSS attributes that I can set and Salesforce is overwriting some that I put in, or if I’m doing something wrong here. Any help would be greatly appreciated, thanks!
What I put in:
What is displayed in the element:
LikeLike
Whoops, it looks like what I copied over may not be showing up.
What I entered:
td style=”border-bottom: 1px solid black; text-align: right; padding:1px;”
What is displayed:
td style=”text-align: right;” rowspan=”1″ colspan=”1″
LikeLike
Hey Tyler,
It should’ve worked. Try using the ‘important’ to force the styling.
Something like this:
td style=”border-bottom: 1px solid black !important; text-align: right; padding:1px !important;”
LikeLike
This is amazing – just what I’ve been looking for!
The only thing I can’t see is evidence of the email going out in Salesforce, I thought I’d be able to see it on the Account record (I’ve got it set for other objects) but I can’t see it anyway.
How would I be able to see the record of the email being sent (ideally in a case)?
LikeLike
You’ll have to manually create an email message record if you want some tracking.
LikeLike
Ok thanks, I’ll look into how to do that 🙂
LikeLike
Feel free to reach out if you’re having issues setting it up. 🙂
LikeLike
Hi this is great thanks and working a treat
However one of the columns I am displaying is a number field and its not showing as two decimal places in the email table i.e. 2.30 shows as 2.3
LikeLike
fixed my decimal places issue
Here’s the trick. You have to
take the “whole numbers” portion
Round down
add the decimal places using text
subtract to get the decimal portion
multiply that by 10^[how many digits you want to force]
pad the left hand side of the digits after the decimal point with zeroes to add up to the number of decimal places you need
text(floor(Field_a__c)) &”.”& lpad(text((Field_a__c-floor(Field_a__c))*100),2,”0″)
In this example, you’ll get output like “157.50” if your number is 157.5, “147.00” if your number is 147, and “127.01” if your number is 127.01.
LikeLike
Thank you for taking the time to post this. It helped me with quite a complicated task, which I would have been clueless to complete without your solution.
LikeLike
This feature is amazing, however I am having two small issues which is stopping us from using it fully:
– the order of the results in the table is not matching the sorting order from the results in Get Records, I need it to sort by date
– the date field is showing as GMT but we’re currently on British Summer Time so they are all out by an hour!
Any advice on how to fix these would make my life 10 times better – thank you!
LikeLike
1. It should show the results based on the record order in collection. I can’t say for certain what’s causing the issue without taking a look at the flow.
2. You can use formula variable to convert datetime to your time zone and use that variable in the table.
LikeLike
It works. Thanks so much for sharing.
LikeLike
This is great! I’m trying to replicate with Orders and Order Products, and I’m getting stuck on the loop. What Collection Variable do I need? Thanks!
LikeLike
Are you trying to do table inside a table?
LikeLike
Hi thank you for the sharing
How do I add a bottom line that sums up?
LikeLike
Hello , Thanks for your above blog. It is amazing. But Can we do it in scheduled flow? can we send multiple emails through flow? how we can store the mapping of table or text template and their respective contact email address? waiting for your response.
LikeLike
You can. Each flow interview should run for 1 contact so no need to store the mapping.
Check this to understand more about Scheduled flows : https://forcepanda.wordpress.com/2021/07/13/scheduled-flows-how-do-they-work/
LikeLike
Thanks a lot for your reply. You are amazing
LikeLike
Hello , I have posted a question on https://forcepanda.wordpress.com/2021/07/13/scheduled-flows-how-do-they-work/comment-page-1/#comment-17622 will you please answer to my query?
LikeLike
This is great! You saved my bacon as I was clueless on how to post a table in an email sent from a flow. Testing it, it works in the sense that the information is found and displayed correctly, but I have a formatting issue as the email doesn’t display a table, just the coding. I’m sure I’m just missing a simple step, so please help me figure it out. Here’s the email below. Thanks!
Unless otherwise indicated by you we have set the account up in our system as follows:
Account Title: W5504
Benchmark: 70% Russell 3000/ + 30% MSCI ACWI ex USA Style Tilt: No Tilt Loss Harvesting: Standard Loss Harvesting Initial Investment Amount: 0
SRI:
Ticker Restrictions: 0
Security Name Ticker Constraint Type Kawashima IndistriesKINDUSNo Buy
• If there are any Dividend Reinvestment Programs (DRIPs) turned on for this account, please instruct the custodian to turn them off as soon as possible.
We will get back to you with and any additional open items once your request and custodial setup have been reviewed. Please note that this confirmation of your new account submission does not mean the account has been fully opened with Aperio.
LikeLike
This is great. How could I update this to include two sets of separate tables in the email? One right after the other?
LikeLike
You could create two template variables and populate them with table data and then create a third template variable which contains previous 2 template variables separated by a line break.
LikeLike
Thanks. Which variable are you referencing?
LikeLike