How to convert excel to a responsive tailwind css table for react and next.js
- Step 1Put the data on the first sheet, headers in row 1 — Only the first sheet is read and row 1 becomes the
<th>labels. Move your data to the first tab if it is elsewhere; there is no sheet picker. - Step 2Drop the .xlsx or .csv onto the tool — SheetJS parses it in your browser. Cells are read as formatted display text, so a date shown as
Mar 15, 2026exports as that string, not a serial number. - Step 3Choose a style for the component — Pick
striped(zebra),bordered(grid lines),compact(text-xsdense), orminimal(no borders). A feature-comparison table usually reads best asbordered; a long data list ascompact. - Step 4Toggle dark mode to match your theme strategy — Dark mode is off by default. When on, it appends
dark:bg-gray-800 dark:text-gray-100to the wrapper, every<th>, and every<tr>. This expects Tailwind'sclassdark-mode strategy (adarkclass toggled high in the tree). - Step 5Convert class to className (or use dangerouslySetInnerHTML) — The snippet uses the HTML
classattribute. For a real.tsxcomponent, either find-and-replaceclass=withclassName=, or render the string via<div dangerouslySetInnerHTML={{ __html: html }} />for static, trusted data. - Step 6Ensure the file is in your Tailwind content globs — Whichever component file holds the markup must be covered by your Tailwind
contentconfiguration, or the build purges the utility classes and the table renders unstyled.
The two options the tool exposes
These are the only controls. There is no class-name editor, no sticky-header toggle, and the output is HTML (class), not JSX (className).
| Option | Values | Default | Effect in a React context |
|---|---|---|---|
style | striped, bordered, compact, minimal | striped | Sets the Tailwind classes on <table>/<th>/<td> — same classes whether you target React or plain HTML |
darkMode | on / off | off | Appends dark: background/text classes; works with Tailwind's class dark-mode strategy used by most Next.js theme toggles |
Embedding the snippet in React / Next.js
The output is HTML, not JSX. Pick the approach that fits your data.
| Approach | When to use it | Caveat |
|---|---|---|
Replace class= with className= | You want real JSX you can edit and extend (add onClick, etc.) | Manual find/replace; also rename any for= if present (none here) |
dangerouslySetInnerHTML | Static, trusted, build-time data you won't edit in JSX | Only for data you control; cells are already HTML-escaped by the tool |
| Server Component returning the HTML string | Next.js App Router, data fetched/embedded at build or request time | Still need className or dangerouslySetInnerHTML to render markup |
Plan limits
Pro-tier feature. Per the excel tool family.
| Plan | Max file size | Max rows |
|---|---|---|
| Free | Not available (Pro required) | — |
| Pro | 50 MB | 100,000 |
| Pro-media | 200 MB | 500,000 |
| Developer | 500 MB | Unlimited |
Cookbook
Snippets oriented to React/Next.js workflows — including the class-to-className conversion you'll do on every paste.
Raw output uses class, not className
Straight from the tool, the snippet is HTML. This is why pasting it unchanged into a JSX return will throw or warn about class.
Tool output (style: striped):
<div class="overflow-x-auto">
<table class="w-full text-sm text-left text-gray-700 border-collapse">
<thead><tr>
<th class="px-4 py-3 bg-gray-100 font-semibold ... text-xs border-b">Feature</th>
</tr></thead>
...
</table>
</div>
React warning if pasted as-is:
Warning: Invalid DOM property `class`. Did you mean `className`?Convert to a JSX component
Find-and-replace class= with className= and wrap in a component. Now it is editable JSX.
export function FeatureTable() {
return (
<div className="overflow-x-auto">
<table className="w-full text-sm text-left text-gray-700 border-collapse">
<thead><tr>
<th className="px-4 py-3 bg-gray-100 font-semibold ... text-xs border-b">Feature</th>
</tr></thead>
<tbody>
<tr className="even:bg-gray-50">
<td className="px-4 py-3 border-b">SSR</td>
</tr>
</tbody>
</table>
</div>
);
}Render static data with dangerouslySetInnerHTML
For build-time data you won't edit in JSX, skip the conversion and inject the HTML string. The tool already escaped cell content, so this is safe for trusted spreadsheet data.
const tableHtml = `<div class="overflow-x-auto">...</div>`; // from the tool
export default function Page() {
return <section dangerouslySetInnerHTML={{ __html: tableHtml }} />;
}Dark-mode compact table for a dashboard
Compact + dark mode. The dark classes land on the wrapper, th, and tr. Pair with a theme toggle that adds the dark class to <html>.
Config: style = compact, darkMode = on
<div class="overflow-x-auto dark:bg-gray-800 dark:text-gray-100">
<table class="w-full text-xs text-left text-gray-600 border-collapse">
<thead><tr>
<th class="px-2 py-1 bg-gray-50 font-medium border-b dark:bg-gray-800 dark:text-gray-100">Metric</th>
</tr></thead>
<tbody>
<tr class="dark:bg-gray-800 dark:text-gray-100">
<td class="px-2 py-1 border-b">Latency</td>
</tr>
</tbody>
</table>
</div>Adding client-side sort after conversion
The snippet is static; the tool adds no JavaScript. To sort, convert to a component, lift the rows into state, and re-render. The tool gives you the markup, not the interactivity.
// after converting to className and mapping rows to state:
const [rows, setRows] = useState(initialRows);
function sortBy(key) {
setRows([...rows].sort((a, b) => a[key].localeCompare(b[key])));
}
// <th onClick={() => sortBy('Feature')} className="... cursor-pointer">Feature</th>Edge cases and what actually happens
Snippet pasted raw into a .tsx return
JSX class errorThe output uses the HTML class attribute. React expects className, so an unconverted paste throws a build error or a console warning. Find-and-replace class= with className=, or render the string through dangerouslySetInnerHTML.
Expecting a reusable React component file
Snippet onlyThe tool outputs HTML markup, not a .tsx component with props, imports, or types. You wrap it in a function component yourself. There is no 'generate React component' option in the UI.
Expecting sort/filter/pagination
Static onlyThe output is static HTML with zero JavaScript. Sorting, filtering, and pagination are not generated. Convert to a component and add state-driven handlers, or use an interactive grid library if you need rich interactivity.
Dark mode classes do nothing
Config issueThe dark: classes only activate under Tailwind's dark-mode strategy. With the default class strategy you must toggle a dark class on a parent (commonly <html>). If your theme uses the media strategy or no dark mode at all, the dark: variants never apply.
Table renders unstyled in production
Purge issueIf the component file (or the string literal holding the HTML) is not in your Tailwind content globs, the production build strips the classes. Add the file to content and rebuild.
Data is on a second sheet
First sheet onlyOnly sheet index 0 is read. Move the data to the first tab or export it as its own file before running the tool.
Duplicate column headers in row 1
PreservedSheetJS suffixes duplicate header names (Name, Name_1), so both columns survive and render distinct <th> cells. Rename in the spreadsheet if you want a clean heading.
Free tier
402 Pro requiredThis is a Pro feature; the Free tier cannot run it. Upgrade to Pro or higher.
Very large table inlined into a page
Slow hydrationEvery row becomes a <tr> with no truncation. A huge table inlined into a Next.js page bloats the HTML payload and slows hydration. Paginate or virtualise rather than rendering tens of thousands of rows at once.
Frequently asked questions
Does the output work in a .tsx file directly?
Not without one change. It is HTML using the class attribute, and JSX requires className. Find-and-replace class= with className= for an editable component, or render the raw string with dangerouslySetInnerHTML for static data.
Is it a React component or just markup?
Just markup — a <div>+<table> snippet. There is no option to generate a .tsx component with props or types. You wrap it in a function component yourself.
Does the output work with Tailwind v3 and v4?
The generated classes are standard Tailwind utilities (w-full, text-sm, px-4, even:bg-gray-50, border-collapse, dark:bg-gray-800, etc.) that exist in both v3 and v4. The tool emits the same strings regardless of your Tailwind version; verify them against your build if you have a heavily customised theme.
Can I make the table sortable?
Not from the tool — the output is static HTML with no JavaScript. Convert it to a component, move the rows into React state, and add onClick sort handlers on the <th> cells, or wrap the <table> in an interactive grid library.
Which sheet and which row does it use?
The first sheet only, with row 1 as the headers (the <th> cells). Other tabs are ignored and there is no sheet picker.
Where exactly do the dark mode classes go?
dark:bg-gray-800 dark:text-gray-100 is appended to the wrapper <div>, every <th>, and every <tr>. It is not added to <td> or the <table> element. The variants only take effect under Tailwind's class dark-mode strategy.
Is dangerouslySetInnerHTML safe here?
For trusted spreadsheet data, yes — the tool already escapes &, <, >, and " in every cell, so injected markup renders as text. Never use it for untrusted third-party content regardless.
Does it preserve formatted dates and numbers?
Yes — cells are read as their formatted display text. A date shown as Mar 15, 2026 exports as that string; it does not become a serial number. Normalise the sheet first if you need raw values (for example with the Excel date standardizer).
Can it read a CSV instead of an xlsx?
Yes — both .xlsx and .csv are accepted, and the CSV's first row is treated as headers.
Does my spreadsheet upload to a server?
No. SheetJS parses the file in your browser and the HTML is generated locally. Nothing is uploaded.
Can I get the data as Python or a tRPC router instead?
Yes — sibling tools in the same family cover that: the Excel to Python generator emits a dict/DataFrame, and the Excel to tRPC router tool scaffolds a router. For an SVG chart use the Excel SVG dataviz tool.
What does the download contain?
A file named table.html holding the exact snippet shown in the preview — the wrapper <div> and the <table>, nothing more.
Privacy first
Every JAD Excel tool runs entirely in your browser using SheetJS and ExcelJS. Your spreadsheets, formulas, and data never leave your device — verified by zero outbound network requests during processing.