Power Automate's built-in Create HTML Table action turns a JSON array into HTML markup in a single step, but the reverse path has no native equivalent: there is no Parse HTML Table action, no row-by-row extractor, and no way to feed table cells into a Condition, an Apply to each, or a Compose without first turning the markup into JSON. When a flow receives table markup from an approval email, a SharePoint page, a Microsoft Forms rich-text answer, or a web request, the rows have to be converted to JSON before any data operation can read them. The fastest way to do that without uploading the markup is to paste the table into a browser-based HTML Table to JSON Converter, choose whether the first row holds the column names, and copy the resulting array of objects straight into a Parse JSON action. The tool reads cell textContent only, rejects any markup that loads scripts or remote resources, repairs duplicate or blank headers deterministically, and emits strings instead of guessed types, so an identifier like 00123 stays 00123. The output is an array of plain JSON objects with one key per column and stable keys across every record, which is exactly the shape a Power Automate Parse JSON schema expects. Because every step runs in the current tab, the markup and the result are not uploaded to any server.

how to convert json to html table in power automate
Turn an HTML Table into JSON for Power Automate

Why Power Automate flows keep ending up with HTML tables

Several common trigger paths produce HTML table markup rather than structured data. When a new email arrives with an HTML body, formatted tables from approval systems, ERP exports, or vendor reports arrive as raw markup inside the Body token. SharePoint "Send an HTTP request" actions and pages returned by the SharePoint REST API often include HTML fragments when a page contains a formatted list view or page content. "When a response is submitted" in Microsoft Forms captures rich-text answers that can carry tables pasted from spreadsheets. Power Apps that return HTML from a Rich Text control or an HTML text control pass table markup into the flow.

In all of these cases, the action set offered by Power Automate covers the easy direction (Create HTML Table, Create CSV table) but not the hard direction. The flow ends up holding a long string of <tr> and <td> tags with no native way to ask for "the third column of the second row." A Compose action can only isolate a substring; a Parse JSON action only accepts JSON. Somewhere in the chain the table has to cross the boundary from markup to data, and a browser-based converter is the simplest place to do that without exposing the markup to a third-party service.

Convert an HTML table to JSON for a Power Automate flow

This is the workflow that fits between the trigger that produced the HTML and the Parse JSON action that will consume it:

  1. Isolate the target table markup in a Compose action. Pass it the Body of the trigger, or, if the email or page contains multiple tables, narrow the string to just the <table>...</table> block you want by bracketing it with indexOf expressions around the first <table> and the matching </table>. Copy the resulting string from the run history.
  2. Open the HTML Table to JSON Converter in a new browser tab.
  3. Paste the markup into the input area.
  4. Choose First row contains headers if the first <tr> holds the column names. Disable the option to keep every row as a record and let the converter generate column_1, column_2, column_3, and further keys.
  5. Enable Trim cell text to strip leading and trailing whitespace from headers and cells while preserving internal spaces and line content.
  6. Click Convert. The first matching table in the input is converted; subsequent tables in the same document are ignored.
  7. Compare the row and column counts reported beneath the output against the source table. They should match the number of rows and cells you saw in the original markup.
  8. Copy the JSON to the clipboard.

The result is a single JSON array of objects. That array can be pasted into the "Generate from sample" box of a Power Automate Parse JSON action, which will infer a schema with one property per column.

What the converter emits and what it deliberately does not

Because Power Automate's Parse JSON schema is unforgiving about key shapes and value types, the converter's behavior is worth knowing in advance.

Behavior Result
Default header source First row becomes the property names
Empty header cell Replaced by a generated column name
Duplicate header names Receive _2, _3, and further suffixes in encounter order
Missing trailing cell Becomes an empty string so every record has the same keys
Extra cell beyond heading count Generates a new column to hold the value
Value types Every cell stays a string; no numeric, boolean, date, or null coercion
Markup, attributes, links, styles Not copied into the JSON output
Rowspan and colspan Not expanded into a rectangular grid
Nested tables Not traversed; remove before conversion
Script, style, image, frame, audio, video, source, link, object, embed, SVG, MathML Input rejected before parsing
Output preview None rendered; only JSON is produced

The header rules matter most for Power Automate because Parse JSON requires every record in the array to share the same key set. By filling missing trailing cells with empty strings and generating extra columns for over-long rows, the converter guarantees that every object in the array has identical keys, which is the precondition for "Generate from sample" to produce a working schema.

The string-only behavior also matters. Power Automate's Parse JSON will not coerce a string into a number, a boolean, or null, and any client system downstream is also free to misread values. By emitting strings, the converter protects identifiers with leading zeros (invoice numbers, postal codes, customer codes), large account references, and locale-specific currency strings from accidental numeric rounding. If the destination schema requires typed values, add an explicit conversion step after the Parse JSON action; the converter does not infer types because textual appearance is not a reliable type declaration.

Limits that matter for Power Automate use cases

Three limits in the converter interact with real Power Automate payloads. The first matching table is converted only, so a document that contains a navigation table, a comparison table, and the data table you actually want will silently ignore the second and third. Use a Compose step to extract just the target <table>...</table> block before pasting. Input is capped at 500,000 characters, which covers most email-body tables comfortably but will reject very long SharePoint page exports; truncate the input upstream if you approach the limit. Malformed HTML is repaired by the browser parser, so if the source markup is missing closing tags or has overlapping cells, the resulting JSON can differ from what you see in the source; inspect the row and column counts and a representative record before trusting the output.

Tables that use rowspan and colspan are also out of scope. The browser row and cell collections identify cells in encountered order, per the WHATWG HTML tables specification, but do not expand visual spans, so a layout that relies on a cell stretching across three columns will produce a single value at the position of the originating cell rather than three aligned entries. Nested tables introduce ambiguous ownership, since a cell inside a row may contain its own <table> with its own headers, and the converter cannot tell which table owns the row. Flatten both cases in the originating application before conversion.

Bring the JSON back into Power Automate safely

Once the array is in the clipboard, the rest is a standard Power Automate pattern. Add a Parse JSON action and paste the copied JSON into "Generate from sample"; Power Automate will infer a schema with one property per column. Reference the fields with expressions like body('Parse_JSON')?['InvoiceNumber'] inside an Apply to each or a Condition. Validate the JSON locally with the JSON Validator before pasting it into Parse JSON so syntax errors are caught before schema inference.

Validate the values that need typed output as well. Parse JSON will hand strings to numeric columns without complaint, but downstream actions like "Create a row" in Dataverse or "Send an HTTP request" to a JSON-typed API may reject them. Add an int() or float() conversion in a Compose step for fields that should be numbers, and keep string fields untouched. Run a quick test with a known input: a representative record should round-trip with identical keys and values, and if it does not, revisit the converter options or the source table markup. If you need to go the other direction and turn the JSON back into HTML for an email body or a Teams message, see Convert JSON to HTML Table in JavaScript Safely, which covers the patterns that map cleanly onto Power Automate's Create HTML Table action.

Privacy and local processing for sensitive payloads

Approval emails and SharePoint page contents routinely contain personal data, internal account numbers, and confidential pricing. The converter processes the markup entirely in the current tab. The browser's DOMParser, documented in MDN's DOMParser reference, reads the input as an inert document without executing scripts or loading remote resources, textContent is read directly from each cell, JSON.stringify formats the result, and the clipboard write happens locally. Nothing is uploaded, and no network request carries the table contents. That local boundary ends at the browser tab itself, so installed extensions can still read the page DOM and a cloud-synced clipboard may carry the JSON to other devices. Remove sensitive rows before pasting if you plan to share a screenshot, an exported file, or a copy of the converter output with anyone else.

Related reading: Convert Text to HTML Paragraphs in Bulk Without a Server.