> ## Documentation Index
> Fetch the complete documentation index at: https://jetxl.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Multiple sheets

> Write several sheets to one workbook, generated in parallel

`write_sheets_arrow` takes a list of sheet dictionaries and generates their XML across threads. You configure each sheet independently, with the same options as a single-sheet write.

```python theme={null}
jet.write_sheets_arrow(
    [
        {"data": df_sales.to_arrow(),  "name": "Sales",  "auto_filter": True},
        {"data": df_costs.to_arrow(),  "name": "Costs",  "freeze_rows": 1},
        {"data": df_profit.to_arrow(), "name": "Profit", "styled_headers": True},
    ],
    "report.xlsx",
    num_threads=3,
)
```

<Warning>
  `num_threads` is required and positional here, with no default. Omitting it raises a `TypeError`. Only `write_sheets_arrow_to_bytes` defaults it, to `1`.
</Warning>

## The sheet dictionary

Two keys are required:

<ParamField path="data" type="Arrow table" required>The sheet contents.</ParamField>
<ParamField path="name" type="str" required>The tab label.</ParamField>

Everything else is any keyword argument from `write_sheet_arrow`, used as a key. Formatting, tables, charts, images, validation, conditional formats and appearance all work, set per sheet:

```python theme={null}
sheets = [
    {
        "data": df_sales.to_arrow(),
        "name": "Sales",
        "styled_headers": True,
        "freeze_rows": 1,
        "column_formats": {"Revenue": "currency"},
        "tables": [{"name": "SalesTable", "start_row": 1, "start_col": 0,
                    "style": "TableStyleMedium9"}],
        "tab_color": "00B050",
    },
    {
        "data": df_costs.to_arrow(),
        "name": "Costs",
        "auto_width": True,
        "conditional_formats": [{
            "start_row": 2, "start_col": 2, "end_row": 100, "end_col": 2,
            "rule_type": "data_bar", "color": "FF0000",
        }],
        "tab_color": "FF0000",
    },
]

jet.write_sheets_arrow(sheets, "advanced.xlsx", num_threads=2)
```

## Choosing a thread count

Parallelism is per sheet, so more threads than sheets buys nothing.

```python theme={null}
import os

num_threads = min(os.cpu_count() or 1, len(sheets))
jet.write_sheets_arrow(sheets, "report.xlsx", num_threads=num_threads)
```

<Note>
  Jetxl shares and deduplicates style definitions across sheets, so repeating the same formatting on every sheet costs far less than it might appear.
</Note>

## When sheets differ in shape

Nothing requires sheets to share columns or row counts. A summary sheet of one row can sit beside a detail sheet of a million:

```python theme={null}
sheets = [
    {"data": df_summary.to_arrow(), "name": "Summary",
     "styled_headers": True, "gridlines_visible": False},
    {"data": df_detail.to_arrow(),  "name": "Detail",
     "auto_filter": True, "freeze_rows": 1, "auto_width": False},
]
```

<Tip>
  Put the summary first. It becomes the sheet that opens, and it's what most readers want.
</Tip>
