Rendering Tables

GFM syntax

In markdown is preferable write table via GFM syntax.

| left   | center | right |
| :----- | :----: | ----: |
| foo    |  bar   |   baz |
| banana | apple  |  kiwi |

will be rendered as:

leftcenterright
foobarbaz
bananaapplekiwi

HTML Literal Tables

If you'll try render the following literal <table /> element:

<table>
  <thead>
    <tr>
      <th>left</th>
      <th align="center">center</th>
      <th align="right">right</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>foo</td>
      <td align="center">bar</td>
      <td align="right">baz</td>
    </tr>
    <tr>
      <td>banana</td>
      <td align="center">apple</td>
      <td align="right">kiwi</td>
    </tr>
  </tbody>
</table>

you'll get the following result:

left center right
foo bar baz
banana apple kiwi
⚠️

Confused by unstyled elements? We explained here, why it's happens.

Dynamic Tables

How to Write

Want to render dynamic table? You can use embeded JavaScript expressions into your table for it:

<table>
  <thead>
    <tr>
      <th>Country</th>
      <th>Flag</th>
    </tr>
  </thead>
  <tbody>
    {[
      { country: 'France', flag: '🇫🇷' },
      { country: 'Ukraine', flag: '🇺🇦' }
    ].map(item => (
      <tr key={item.country}>
        <td>{item.country}</td>
        <td>{item.flag}</td>
      </tr>
    ))}
  </tbody>
</table>

will be rendered as:

Country Flag
France🇫🇷
Ukraine🇺🇦
⚠️

Confused by unstyled elements? We explain below 👇, why it's happens.

Unexpected Result

Table looks different compared to GFM syntax table:

  1. only children of table body <tbody /> is styled

  2. table header is unstyled

  3. table doesn't have margin top

Why This Happens

MDX2 doesn't replace literal HTML elements with <MDXProvider />.

Adam Wathan, creator of Tailwind CSS submitted an issue in MDX2 to have some an escape hatch that we can name like:

please only transform markdown tags, not literal HTML tags

Table header looks unstyled since not replaced with Nextra's MDX components <tr />, <th /> and <td />, for the same reason <table /> literal is not replaced and doesn't have default margin-top aka mt-6.

Ways to Fix It

One-Time Fix

Just wrap your table with curly braces { and }, e.g.

{<table>
 ...
</table>}

Changing Default Behaviour

If this thing is still confusing for you, and you want to use regular literal HTML elements for your tables, do the following:

  1. Install remark-mdx-disable-explicit-jsx package
pnpm add remark-mdx-disable-explicit-jsx
  1. Setup plugin in nextra function inside next.config.mjs file
import nextra from 'nextra'
import remarkMdxDisableExplicitJsx from 'remark-mdx-disable-explicit-jsx'
 
const withNextra = nextra({
  theme: 'nextra-theme-docs',
  themeConfig: './theme.config.tsx',
  remarkPlugins: [
    [
      remarkMdxDisableExplicitJsx,
      { whiteList: ['table', 'thead', 'tbody', 'tr', 'th', 'td'] }
    ]
  ]
})
 
export default withNextra()