Summary
The only documented way to write an example with more than one table is the "hug" — a step paragraph placed directly after a table with no blank line between them:
A single example can carry a whole Given → table → When → doc string flow. When a following step paragraph hugs a table or fence with no blank line between them, it merges into the same example rather than starting a new one.
— Examples › Tables and doc strings
That construct is not valid GFM. In GFM a table body runs until a blank line, so the hugging paragraph is parsed as another table row. The consequence is that the one layout Varar documents for multi-table examples cannot survive a markdown formatter, and does not render as written on GitHub — against the promise on Varar for Cucumber users:
Your specs live in ordinary Markdown, so they render on GitHub, in your docs site, in your editor.
I hit this porting a real 12-file Cucumber suite to .md, where every scenario has 2–4 tables.
Repro
Given the following users have been imported:
| email | name |
| ----- | ---- |
| a@b.c | Ada |
And the following assets have been imported:
| name |
| ----- |
| Moose |
Then the basket should contain the following line items:
Varar plans this as one example with three steps, which is what you want. Now run it through any GFM parser.
marked (GFM), abridged:
<tbody>
<tr><td><a href="mailto:a@b.c">a@b.c</a></td><td>Ada</td></tr>
<tr><td>And the following assets have been imported:</td><td></td></tr>
</tbody>
The step became a <td>. Same for the third step. On GitHub the reader sees two tables with a junk final row and no steps.
prettier --write does the corresponding thing to the source — it re-emits the step as a row, padding the column to fit:
-| owner@example.com | Owen | Owner |
-| osiris@example.com | Osiris | Owner |
-And the following assets have been imported:
+| owner@example.com | Owen | Owner |
+| osiris@example.com | Osiris | Owner |
+| And the following assets have been imported: |
After that, the spec no longer means what it did. In my suite a 4-step example collapsed to its first step and the run failed:
❯ |varar| src/features/basket.md (1 test | 1 failed)
× Given the following users have been imported:
That is the good outcome, because the steps that vanished were When/Then and the example failed loudly. Reorder so the surviving step is a Given and you get a passing example that asserts nothing — with no baseline recorded, the drift gate can't see it either (see #57 §10 for why a baseline is unreachable when the CLI can't host your steps).
So: running the formatter that the rest of the repo runs silently rewrites the spec.
There is currently no GFM-safe alternative
I tried all three candidate units the reference names ("a paragraph — or a list item, or a blockquote") on the same scenario:
| Layout |
Valid GFM |
Varar result |
| Paragraph + hug |
❌ steps parse as table rows |
✅ 1 example, 4 steps |
| List item with indented blocks |
✅ |
❌ 4 separate examples — state not shared, all fail |
| Blockquote |
✅ |
❌ 1 example, but the nested tables are flattened into the example name and never parsed as tables |
The blockquote case is the interesting one. Grouping already works — the whole quote became a single example. What's missing is that the scanner flattens the quote's contents to text instead of descending into its child blocks, so the tables are lost:
FAIL src/features/basket.md > Given the following users have been imported: | email | firstName | … | | ------ | … | | owner@example.com | Owen | … And the following assets have been imported: | merchantId | …
The list-item case is the mirror image: the blocks are parsed correctly but each paragraph starts a new example, so state from the Given never reaches the Then.
Suggested directions
Roughly in order of how much I'd want them:
-
Make a list item a real container. One list item = one example; every block inside it (paragraphs, tables, fences) belongs to that example. This needs no new syntax, is the GFM-idiomatic way to group blocks, is prettier-stable, and renders correctly on GitHub. It also reads well — a scenario is a bulleted item whose steps are its paragraphs.
-
Descend into blockquotes. Probably the smallest diff, since grouping already behaves. The scanner just needs to parse the quote's children as blocks rather than flattening them to a sentence.
-
Let a blank line survive between an attached table and a continuing step paragraph. Keeps today's paragraph model and today's files working. The continuation rule would need care so two genuinely independent examples don't merge — "continue only when the previous block was a table/fence consumed by this example, and the next paragraph has a sentence that matches a step" is consistent with the existing no-keyword-sniffing design, but it does change the meaning of table-then-new-example documents.
Whatever the mechanism, I'd suggest the reference stop recommending the hug, since it can't survive contact with a formatter.
Workaround meanwhile
Add the spec globs to .prettierignore (and equivalents for markdownlint / fmt-on-save). That keeps the hug intact but leaves the GitHub rendering wrong, so it's a stopgap rather than a fix.
Environment: @varar/varar / @varar/vitest 0.6.1, marked 18.0.7, prettier 3.9.6.
Summary
The only documented way to write an example with more than one table is the "hug" — a step paragraph placed directly after a table with no blank line between them:
That construct is not valid GFM. In GFM a table body runs until a blank line, so the hugging paragraph is parsed as another table row. The consequence is that the one layout Varar documents for multi-table examples cannot survive a markdown formatter, and does not render as written on GitHub — against the promise on Varar for Cucumber users:
I hit this porting a real 12-file Cucumber suite to
.md, where every scenario has 2–4 tables.Repro
Varar plans this as one example with three steps, which is what you want. Now run it through any GFM parser.
marked(GFM), abridged:The step became a
<td>. Same for the third step. On GitHub the reader sees two tables with a junk final row and no steps.prettier --writedoes the corresponding thing to the source — it re-emits the step as a row, padding the column to fit:After that, the spec no longer means what it did. In my suite a 4-step example collapsed to its first step and the run failed:
That is the good outcome, because the steps that vanished were
When/Thenand the example failed loudly. Reorder so the surviving step is aGivenand you get a passing example that asserts nothing — with no baseline recorded, the drift gate can't see it either (see #57 §10 for why a baseline is unreachable when the CLI can't host your steps).So: running the formatter that the rest of the repo runs silently rewrites the spec.
There is currently no GFM-safe alternative
I tried all three candidate units the reference names ("a paragraph — or a list item, or a blockquote") on the same scenario:
The blockquote case is the interesting one. Grouping already works — the whole quote became a single example. What's missing is that the scanner flattens the quote's contents to text instead of descending into its child blocks, so the tables are lost:
The list-item case is the mirror image: the blocks are parsed correctly but each paragraph starts a new example, so
statefrom theGivennever reaches theThen.Suggested directions
Roughly in order of how much I'd want them:
Make a list item a real container. One list item = one example; every block inside it (paragraphs, tables, fences) belongs to that example. This needs no new syntax, is the GFM-idiomatic way to group blocks, is prettier-stable, and renders correctly on GitHub. It also reads well — a scenario is a bulleted item whose steps are its paragraphs.
Descend into blockquotes. Probably the smallest diff, since grouping already behaves. The scanner just needs to parse the quote's children as blocks rather than flattening them to a sentence.
Let a blank line survive between an attached table and a continuing step paragraph. Keeps today's paragraph model and today's files working. The continuation rule would need care so two genuinely independent examples don't merge — "continue only when the previous block was a table/fence consumed by this example, and the next paragraph has a sentence that matches a step" is consistent with the existing no-keyword-sniffing design, but it does change the meaning of table-then-new-example documents.
Whatever the mechanism, I'd suggest the reference stop recommending the hug, since it can't survive contact with a formatter.
Workaround meanwhile
Add the spec globs to
.prettierignore(and equivalents for markdownlint /fmt-on-save). That keeps the hug intact but leaves the GitHub rendering wrong, so it's a stopgap rather than a fix.Environment:
@varar/varar/@varar/vitest0.6.1,marked18.0.7,prettier3.9.6.