Blazingly fast Excel (XLSX) writer for Python, powered by Rust
Jetxl is a high-performance library for creating Excel files from Python with native support for Arrow, Polars, and Pandas DataFrames. Built from the ground up in Rust for maximum speed and efficiency.
- π Ultra-fast: 5-40x faster than other Python Excel libraries
- π Zero-copy Arrow integration: Direct DataFrame β Excel with no intermediate conversions
- π¨ Rich formatting: Fonts, colors, borders, alignment, number formats
- π Advanced features: Conditional formatting, data validation, formulas, hyperlinks, Excel tables, charts, images
- π§΅ Multi-threaded: Parallel sheet generation for multi-sheet workbooks
- πΎ Memory efficient: Streaming XML generation with minimal memory overhead
- π»ββοΈπΌ Framework agnostic: Works seamlessly with Polars, Pandas, PyArrow, and native Python dicts
Benchmark environment: Python 3.13, AMD Ryzen 9 7900x, 64 GB RAM
At the time of the test, the following library versions were used:
- Jetxl: 0.1.0
- Polars: 1.37.0
- Pandas: 2.3.3
- Pyexcelerate: 0.13.0
- Rustpy-xlsxwriter: 0.7.0
- Openpyxl: 3.1.5
- Xlsxwriter: 3.2.9
| Library | 1M Rows | Speedup | Throughput | Memory |
|---|---|---|---|---|
| jetxl (arrow) | 2.06s | 1.0x | 526K rows/s | ~0 MB |
| jetxl (dict) | 3.57s | 1.7x slower | 286K rows/s | ~0 MB |
| xlsxwriter | 10.05s | 4.9x slower | 96K rows/s | 0.4 MB |
| rustpy_xlsxwriter | 11.27s | 5.5x slower | 89K rows/s | - |
| pyexcelerate | 35.55s | 17x slower | 28K rows/s | - |
| polars.write_excel | 40.85s | 20x slower | 27K rows/s | 2.1 GB |
| openpyxl | 56.25s | 27x slower | 18K rows/s | 0.4 MB |
| pandas+xlsxwriter | 56.30s | 27x slower | 18K rows/s | - |
| pandas+openpyxl | 83.42s | 40x slower | 12K rows/s | - |
The chart below shows the execution time comparison with popular libraries for a single sheet file against Jetxl. Performance will vary on different hardware.

| Library | 10K rows | 100K rows | 1M rows |
|---|---|---|---|
| jetxl (arrow) | 0.022 | 0.19 | 2.06 |
| jetxl (dict) | 0.032 | 0.31 | 3.57 |
| rustpy_xlsxwriter | 0.107 | 1.06 | 11.27 |
| xlsxwriter | 0.112 | 1.00 | 10.05 |
| pyexcelerate | 0.333 | 3.86 | 35.55 |
| polars.write_excel | 0.349 | 3.53 | 40.85 |
| openpyxl | 0.516 | 5.10 | 56.25 |
| pandas+xlsxwriter | 0.523 | 5.59 | 56.30 |
| pandas+openpyxl | 0.756 | 8.33 | 83.42 |
pip install jetxl
# Install with uv (recommended)
# uv pip install jetxlImportant
Jetxl is an experimental xlsx writer in its current state. There are still bugs and breaking changes that can happen. Existing functionality is subject to change.
import polars as pl
import jetxl as jet
# Create a DataFrame
df = pl.DataFrame({
"Name": ["Alice", "Bob", "Charlie"],
"Age": [25, 30, 35],
"Salary": [50000.0, 60000.0, 75000.0]
})
# Write to Excel (requires to_arrow() conversion)
jet.write_sheet_arrow(df.to_arrow(), "output.xlsx")import pandas as pd
import jetxl as jet
df = pd.DataFrame({
"Name": ["Alice", "Bob", "Charlie"],
"Age": [25, 30, 35],
"Salary": [50000.0, 60000.0, 75000.0]
})
# Convert to Arrow for zero-copy performance
jet.write_sheet_arrow(df.to_arrow(), "output.xlsx")import pyarrow as pa
import jetxl as jet
# Create an Arrow table
table = pa.table({
"Name": ["Alice", "Bob", "Charlie"],
"Age": [25, 30, 35],
"Salary": [50000.0, 60000.0, 75000.0]
})
# Write directly from Arrow table
jet.write_sheet_arrow(table, "output.xlsx")import jetxl as jet
data = {
"Name": ["Alice", "Bob", "Charlie"],
"Age": [25, 30, 35],
"Salary": [50000.0, 60000.0, 75000.0]
}
jet.write_sheet(data, "output.xlsx")Write a single sheet from Arrow-compatible data (Polars, PyArrow, Pandas).
jet.write_sheet_arrow(
arrow_data, # DataFrame or Arrow RecordBatch
filename, # Output file path
sheet_name=None, # Sheet name (default: "Sheet1")
auto_filter=False, # Enable autofilter on headers
freeze_rows=0, # Number of rows to freeze
freeze_cols=0, # Number of columns to freeze
auto_width=False, # Auto-calculate column widths
styled_headers=False, # Apply bold styling to headers
write_header_row=True, # Write column names as first row
column_widths=None, # Dict[str, float|str] - manual widths
column_formats=None, # Dict[str, str] - number formats
merge_cells=None, # List[(row, col, row, col)] - merge ranges
data_validations=None, # List[dict] - validation rules
hyperlinks=None, # List[(row, col, url, display)]
row_heights=None, # Dict[int, float] - row heights
cell_styles=None, # List[dict] - individual cell styles
formulas=None, # List[(row, col, formula, cached_value)]
conditional_formats=None, # List[dict] - conditional formatting
tables=None, # List[dict] - Excel table definitions
charts=None, # List[dict] - Excel chart definitions
images=None, # List[dict] - Excel image definitions
gridlines_visible=True, # Show worksheet gridlines
zoom_scale=None, # Zoom percentage 10-400
tab_color=None, # Sheet tab color (ARGB hex)
default_row_height=None, # Default row height in points
hidden_columns=None, # List[int] - column indices to hide
hidden_rows=None, # List[int] - row indices to hide
right_to_left=False, # Enable RTL layout
data_start_row=0, # Skip rows for auto-width calculation
header_content=None # List[(row, col, text)] - custom header rows
)Write multiple sheets with parallel processing. Full feature parity with write_sheet_arrow() - each sheet supports all formatting options independently.
jet.write_sheets_arrow(
sheets, # List[dict] with data, name, and any formatting options
filename, # Output file path
num_threads # Parallel threads for XML generation
)Each sheet dict supports all write_sheet_arrow() parameters:
{
"data": arrow_data, # Required: Arrow Table/RecordBatch
"name": "Sheet1", # Required: Sheet name
# All write_sheet_arrow() options available:
"auto_filter": bool,
"freeze_rows": int,
"freeze_cols": int,
"auto_width": bool,
"styled_headers": bool,
"write_header_row": bool,
"column_widths": Dict[str, float|str],
"column_formats": Dict[str, str],
"merge_cells": List[Tuple[int, int, int, int]],
"data_validations": List[dict],
"hyperlinks": List[Tuple[int, int, str, str]],
"row_heights": Dict[int, float],
"cell_styles": List[dict],
"formulas": List[Tuple[int, int, str, str]],
"conditional_formats": List[dict],
"tables": List[dict],
"charts": List[dict],
"images": List[dict],
"gridlines_visible": bool,
"zoom_scale": int,
"tab_color": str,
"default_row_height": float,
"hidden_columns": List[int],
"hidden_rows": List[int],
"right_to_left": bool,
"data_start_row": int,
"header_content": List[Tuple[int, int, str]]
}Example with independent sheet configurations:
sheets = [
{
"data": df_sales.to_arrow(),
"name": "Sales",
"styled_headers": True,
"tables": [{"name": "SalesTable", ...}],
"charts": [{"chart_type": "column", ...}],
"tab_color": "FF00B050"
},
{
"data": df_costs.to_arrow(),
"name": "Costs",
"conditional_formats": [{...}],
"hidden_columns": [2, 3],
"tab_color": "FFFF0000"
}
]
jet.write_sheets_arrow(sheets, "report.xlsx", num_threads=4)Returns Excel file as bytes instead of writing to disk. Identical parameters to write_sheet_arrow() except returns bytes instead of writing to a file.
excel_bytes = jet.write_sheet_arrow_to_bytes(
arrow_data, # DataFrame or Arrow RecordBatch
sheet_name=None, # Sheet name (default: "Sheet1")
auto_filter=False, # Enable autofilter on headers
freeze_rows=0, # Number of rows to freeze
freeze_cols=0, # Number of columns to freeze
auto_width=False, # Auto-calculate column widths
styled_headers=False, # Apply bold styling to headers
write_header_row=True, # Write column names as first row
column_widths=None, # Dict[str, float|str] - manual widths
column_formats=None, # Dict[str, str] - number formats
merge_cells=None, # List[(row, col, row, col)] - merge ranges
data_validations=None, # List[dict] - validation rules
hyperlinks=None, # List[(row, col, url, display)]
row_heights=None, # Dict[int, float] - row heights
cell_styles=None, # List[dict] - individual cell styles
formulas=None, # List[(row, col, formula, cached_value)]
conditional_formats=None, # List[dict] - conditional formatting
tables=None, # List[dict] - Excel table definitions
charts=None, # List[dict] - Excel chart definitions
images=None, # List[dict] - Excel image definitions
gridlines_visible=True, # Show worksheet gridlines
zoom_scale=None, # Zoom percentage 10-400
tab_color=None, # Sheet tab color (ARGB hex)
default_row_height=None, # Default row height in points
hidden_columns=None, # List[int] - column indices to hide
hidden_rows=None, # List[int] - row indices to hide
right_to_left=False, # Enable RTL layout
data_start_row=0, # Skip rows for auto-width calculation
header_content=None # List[(row, col, text)] - custom header rows
)Use Cases:
- Web APIs and HTTP responses
- Cloud functions (AWS Lambda, Google Cloud Functions)
- Streaming scenarios
- In-memory processing
- Base64 encoding for JSON APIs
Examples:
import polars as pl
import jetxl as jet
df = pl.DataFrame({
"Name": ["Alice", "Bob"],
"Age": [25, 30],
"Salary": [50000, 60000]
})
# Generate Excel in memory
excel_bytes = jet.write_sheet_arrow_to_bytes(
df.to_arrow(),
sheet_name="Employees",
styled_headers=True,
auto_width=True
)
# Save to file
with open("output.xlsx", "wb") as f:
f.write(excel_bytes)
# Or use in web framework (Flask)
from flask import Response
@app.route('/download')
def download():
excel_bytes = jet.write_sheet_arrow_to_bytes(df.to_arrow())
return Response(
excel_bytes,
mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
headers={'Content-Disposition': 'attachment;filename=data.xlsx'}
)
# Or base64 encode for API
import base64
encoded = base64.b64encode(excel_bytes).decode('utf-8')
# Or return from Lambda
def lambda_handler(event, context):
excel_bytes = jet.write_sheet_arrow_to_bytes(df.to_arrow())
return {
'statusCode': 200,
'body': base64.b64encode(excel_bytes).decode('utf-8'),
'isBase64Encoded': True,
'headers': {
'Content-Type': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
}
}Write multiple sheets to bytes. Identical to write_sheets_arrow() but returns bytes.
excel_bytes = jet.write_sheets_arrow_to_bytes(
sheets, # List[dict] with data, name, and any formatting options
num_threads=1 # Parallel threads for XML generation
)Example:
sheets = [
{
"data": df1.to_arrow(),
"name": "Sales",
"styled_headers": True,
"freeze_rows": 1
},
{
"data": df2.to_arrow(),
"name": "Costs",
"auto_width": True
}
]
# Generate multi-sheet Excel in memory
excel_bytes = jet.write_sheets_arrow_to_bytes(sheets, num_threads=2)
# FastAPI example
from fastapi.responses import Response
@app.get("/report")
async def generate_report():
excel_bytes = jet.write_sheets_arrow_to_bytes(sheets, num_threads=2)
return Response(
content=excel_bytes,
media_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
headers={'Content-Disposition': 'attachment; filename=report.xlsx'}
)
# S3 upload without local file
import boto3
s3 = boto3.client('s3')
s3.put_object(
Bucket='my-bucket',
Key='reports/monthly.xlsx',
Body=excel_bytes,
ContentType='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
)jet.write_sheet(
columns, # Dict[str, List] - column name to values
filename, # Output file path
sheet_name=None, # Sheet name
charts=None # List[dict] - Excel chart definitions
)sheets = [
{"name": "Sales", "columns": sales_data},
{"name": "Expenses", "columns": expenses_data}
]
jet.write_sheets(sheets, "output.xlsx", num_threads=4)import polars as pl
import jetxl as jet
df = pl.DataFrame({
"Product": ["Apple", "Banana", "Cherry"],
"Price": [1.50, 0.75, 2.25],
"Quantity": [100, 150, 80]
})
jet.write_sheet_arrow(
df.to_arrow(),
"formatted.xlsx",
auto_filter=True, # Add filter dropdowns
freeze_rows=1, # Freeze header row
styled_headers=True, # Bold headers
auto_width=True # Auto-size columns
)
# Without headers (data only)
jet.write_sheet_arrow(
df.to_arrow(),
"no_headers.xlsx",
write_header_row=False # Skip writing column names
)Jetxl supports both built-in format shortcuts and custom Excel format codes for complete control over number display.
jet.write_sheet_arrow(
df.to_arrow(),
"formatted.xlsx",
column_formats={
"Price": "currency", # $#,##0.00
"Quantity": "integer", # 0
"Growth": "percentage", # 0%
"Timestamp": "datetime", # yyyy-mm-dd hh:mm:ss
"Score": "decimal2", # 0.00
"Rate": "scientific", # 0.00E+00
"Measurement": "fraction" # # ?/?
}
)Available built-in formats:
general- Default formattinginteger- Whole numbers (0)decimal2- Two decimal places (0.00)decimal4- Four decimal places (0.0000)percentage- Percentage (0%)percentage_decimal- Percentage with decimal (0.00%)percentage_integer- Percentage as integer (0%)currency- Currency ($#,##0.00)currency_rounded- Rounded currency ($#,##0)date- Date (yyyy-mm-dd)datetime- Date and time (yyyy-mm-dd hh:mm:ss)time- Time (hh:mm:ss)scientific- Scientific notation (0.00E+00)fraction- Fraction (# ?/?)fraction_two_digits- Fraction with 2 digits (# ??/??)thousands- Thousands separator (#,##0)
Any string not matching a built-in format becomes a custom Excel format code, giving you full control:
column_formats = {
# Accounting format with negative in parentheses
"Amount": "$#,##0.00_);[Red]($#,##0.00)",
# Thousands with 'K' suffix
"Visitors": "#,##0,\"K\"",
# Millions with 'M' suffix
"Revenue": "$#,##0.0,,\"M\"",
# Custom date format
"Date": "dddd, mmmm dd, yyyy",
# Conditional coloring
"Change": "[Green]#,##0;[Red]-#,##0;[Blue]0",
# Fractions in sixteenths
"Measurement": "# ?/16",
# Phone numbers
"Phone": "(###) ###-####",
# Zero-padded IDs
"ID": "00000",
# Hide zeros
"Optional": "#,##0;-#,##0;\"\""
}Excel format codes use this structure:
[Positive];[Negative];[Zero];[Text]
Format symbols:
0- Digit placeholder (shows 0 if no digit)#- Digit placeholder (shows nothing if no digit)?- Digit placeholder (adds space for alignment).- Decimal point,- Thousands separator (or divider in millions/thousands)%- Multiply by 100 and show percent signE+E-- Scientific notation"text"- Literal text in quotes@- Text placeholder[Color]- Color code (Red, Blue, Green, etc.)[>=100]- Conditional formatting
Scaling numbers:
- One comma
,after number divides by 1,000 - Two commas
,,divide by 1,000,000 - Example:
#,##0,shows 1500 as "2" (rounded thousands) - Example:
#,##0.0,,shows 5000000 as "5.0" (millions)
import polars as pl
import jetxl as jet
df = pl.DataFrame({
"Revenue": [1500000, 500000, 75000],
"Change": [150, -75, 0],
"Ratio": [0.333, 0.125, 0.875],
"Code": [1, 42, 999],
"Date": ["2024-01-15", "2024-02-20", "2024-03-25"]
})
jet.write_sheet_arrow(
df.to_arrow(),
"custom_formats.xlsx",
column_formats={
# Show millions with conditional formatting
"Revenue": "[>=1000000]$#,##0.0,,\"M\";[>=1000]$#,##0,\"K\";$#,##0",
# Color-coded changes with +/- indicators
"Change": "[Green]+#,##0;[Red]-#,##0;[Blue]0",
# Fractions with fallback
"Ratio": "# ?/?;-# ?/?;\"N/A\"",
# Zero-padded codes
"Code": "000000",
# Custom date format
"Date": "dddd, mmmm dd, yyyy"
}
)The easiest way to create custom formats:
- Open Excel and format a cell manually
- Right-click β Format Cells β Custom
- Copy the format code from the "Type:" field
- Use that exact string in Jetxl
- No validation: Custom format codes aren't validated client-side. Invalid codes may cause Excel errors when opening the file.
- XML escaping: Special characters (
<,>,&,",') are automatically escaped - you don't need to worry about them. - Length limit: Format codes are limited to ~255 characters (Excel limitation).
- Compatibility: Some advanced features (locale codes, DBNum) may not work in all Excel versions.
- Color names: Limited to Excel's built-in set:
[Red],[Blue],[Green],[Yellow],[Cyan],[Magenta],[White],[Black],[Color1]-[Color56].
Reference: Excel Number Format Codes - Microsoft
Automatically scale numbers based on magnitude:
# Show millions, thousands, or regular numbers
column_formats = {
"Value": "[>=1000000]#,##0.0,,\"M\";[>=1000]#,##0.0,\"K\";#,##0"
}
# 5000000 β "5.0M"
# 15000 β "15.0K"
# 500 β "500"Display custom text based on values:
column_formats = {
"Status": "[=1]\"β Complete\";[=0]\"β Pending\";\"Unknown\"",
"Grade": "[>=90]\"A\";[>=80]\"B\";[>=70]\"C\";\"F\""
}Professional financial formatting:
column_formats = {
# Negative in parentheses, aligned decimals
"P&L": "_($* #,##0.00_);_($* (#,##0.00);_($* \"-\"??_);_(@_)",
# Simple accounting with red negatives
"Balance": "$#,##0.00_);[Red]($#,##0.00)"
}column_formats = {
"FullDate": "dddd, mmmm dd, yyyy", # Monday, January 15, 2024
"ShortDate": "mm/dd/yy", # 01/15/24
"MonthYear": "mmmm yyyy", # January 2024
"Quarter": "\"Q\"Q yyyy", # Q1 2024
"TimeOnly": "h:mm AM/PM", # 3:45 PM
"Timestamp": "yyyy-mm-dd hh:mm:ss" # 2024-01-15 15:45:30
}column_formats = {
"Inches": "# ?/16\"", # Fractions in sixteenths with inch mark
"Simple": "# ?/?", # Simplest fraction
"Eighths": "# ?/8", # Fractions in eighths
"Mixed": "# ??/??", # Up to two-digit fractions
"Feet": "#' ?/16\"", # 5' 3/16"
}column_formats = {
"Basic": "0%", # 15%
"OneDecimal": "0.0%", # 15.7%
"TwoDecimal": "0.00%", # 15.73%
"WithSign": "+0.0%;-0.0%;0%", # +15.7%, -3.2%, 0%
}# Manual column widths
jet.write_sheet_arrow(
df.to_arrow(),
"sized.xlsx",
column_widths={
"Product": 20.0, # 20 character units
"Description": 50.0,
"Price": 12.0
},
row_heights={
1: 25.0, # Header row height
2: 18.0, # First data row
5: 30.0 # Fifth row
}
)
# Column widths in pixels (converted automatically)
jet.write_sheet_arrow(
df.to_arrow(),
"pixel_widths.xlsx",
column_widths={
"Name": "150px", # 150 pixels
"Email": "200px",
"Status": "80px"
}
)
# Mix of manual and auto
jet.write_sheet_arrow(
df.to_arrow(),
"mixed_widths.xlsx",
auto_width=True, # Auto-calculate most columns
column_widths={
"ID": 8.0, # Override: fixed width for ID
"Notes": 60.0 # Override: extra wide for notes
}
)Column Width Units:
- Float (e.g.,
20.0) - Excel character units (width of '0' in standard font) - String with "px" (e.g.,
"150px") - Pixels (converted to character units) "auto"- Calculate from content (same asauto_width=True)
cell_styles = [
{
"row": 2,
"col": 1,
"font": {
"bold": True,
"italic": False,
"size": 14.0,
"color": "FFFF0000", # Red (ARGB format: AA=alpha, RR=red, GG=green, BB=blue)
"name": "Arial"
},
"fill": {
"pattern": "solid", # Options: "solid", "gray125", "none"
"fg_color": "FFFFFF00", # Yellow
"bg_color": None
},
"border": {
"left": {"style": "thin", "color": "FF000000"},
"right": {"style": "thick", "color": "FF000000"},
"top": {"style": "medium", "color": "FF000000"},
"bottom": {"style": "double", "color": "FF000000"}
},
"alignment": {
"horizontal": "center", # left, center, right, justify
"vertical": "center", # top, center, bottom
"wrap_text": True,
"text_rotation": 45 # 0-180 degrees, 255 for vertical
},
"number_format": "currency"
}
]
jet.write_sheet_arrow(df.to_arrow(), "styled.xlsx", cell_styles=cell_styles)Rotate text in cells for compact headers or labels:
cell_styles = [{
"row": 1,
"col": 0,
"alignment": {
"horizontal": "center",
"vertical": "center",
"text_rotation": 45 # 0-180 degrees, or 255 for vertical text
}
}]
jet.write_sheet_arrow(df.to_arrow(), "rotated.xlsx", cell_styles=cell_styles)Rotation values:
0- No rotation (default)1-90- Counterclockwise rotation91-180- Clockwise rotation (91 = -89Β°)255- Vertical text (top to bottom)
Excel supports different fill patterns:
# Solid fill (most common)
cell_styles = [{
"row": 2,
"col": 0,
"fill": {
"pattern": "solid",
"fg_color": "FFFFFF00" # Yellow
}
}]
# Gray pattern (subtle shading)
cell_styles = [{
"row": 2,
"col": 0,
"fill": {
"pattern": "gray125",
"fg_color": "FFD9D9D9" # Light gray
}
}]
# No fill (transparent)
cell_styles = [{
"row": 2,
"col": 0,
"fill": {
"pattern": "none"
}
}]Apply different border styles to all four sides:
cell_styles = [{
"row": 2,
"col": 0,
"border": {
"left": {"style": "thin", "color": "FF000000"},
"right": {"style": "medium", "color": "FF000000"},
"top": {"style": "thick", "color": "FF0070C0"},
"bottom": {"style": "double", "color": "FF000000"}
}
}]
jet.write_sheet_arrow(df.to_arrow(), "borders.xlsx", cell_styles=cell_styles)Available border styles:
"thin"- Standard thin line"medium"- Medium weight line"thick"- Thick line"double"- Double line"dotted"- Dotted line"dashed"- Dashed line
Color Format Guide:
- Colors use ARGB hexadecimal format:
AARRGGBB AA= Alpha (transparency):FF= fully opaque,00= fully transparentRR= Red component:00= no red,FF= maximum redGG= Green component:00= no green,FF= maximum greenBB= Blue component:00= no blue,FF= maximum blue
Common colors: FFFF0000 (red), FF00FF00 (green), FF0000FF (blue), FFFFFF00 (yellow), FF000000 (black), FFFFFFFF (white)
For more colors and an interactive picker, see the External Resources section below.
Create formatted Excel tables with built-in styles, sorting, and filtering capabilities.
tables = [{
"name": "ProductTable",
"display_name": "Product Data",
"start_row": 1,
"start_col": 0,
"end_row": 0, # NEW: 0 means auto-calculate from data
"end_col": 0, # NEW: 0 means auto-calculate from data
"style": "TableStyleMedium2"
}]
jet.write_sheet_arrow(df.to_arrow(), "table.xlsx", tables=tables)Let Jetxl automatically calculate table dimensions based on your DataFrame:
import polars as pl
import jetxl as jet
df = pl.DataFrame({
"Product": ["A", "B", "C", "D", "E"], # 5 rows
"Price": [10, 20, 30, 40, 50],
"Qty": [100, 200, 150, 300, 250] # 3 columns
})
tables = [{
"name": "AutoTable",
"start_row": 1, # Table starts at row 1 (header)
"start_col": 0, # Column A
"end_row": 0, # Auto: becomes 6 (1 header + 5 data rows)
"end_col": 0, # Auto: becomes 2 (columns A, B, C = indices 0, 1, 2)
"style": "TableStyleMedium2"
}]
jet.write_sheet_arrow(df.to_arrow(), "auto_table.xlsx", tables=tables)Manual vs Auto-Sizing:
# Manual (explicit range)
table = {
"name": "ManualTable",
"start_row": 1,
"start_col": 0,
"end_row": 100, # Exactly 100 rows
"end_col": 5 # Columns A-F
}
# Auto (adapts to DataFrame)
table = {
"name": "AutoTable",
"start_row": 1,
"start_col": 0,
"end_row": 0, # Uses all DataFrame rows
"end_col": 0 # Uses all DataFrame columns
}
# Mixed (partial auto)
table = {
"name": "MixedTable",
"start_row": 1,
"start_col": 0,
"end_row": 50, # Fixed 50 rows
"end_col": 0 # Auto-calculate columns
}Auto-calculation rules:
end_row = 0β calculated asstart_row + num_data_rowsend_col = 0β calculated asstart_col + num_columns - 1- If table starts after row 1, a header row is automatically inserted
- Manual values (non-zero) are used as-is
Excel provides many built-in table styles that you can use with Jetxl. The styles are organized into three categories:
Light Table Styles (Minimal emphasis, subtle colors)
TableStyleLight1throughTableStyleLight21- Best for: Professional reports, financial statements, clean presentations
Medium Table Styles (Moderate emphasis, balanced design)
TableStyleMedium1throughTableStyleMedium28- Best for: Data analysis, dashboards, general-purpose tables
Dark Table Styles (Strong emphasis, high contrast)
TableStyleDark1throughTableStyleDark11- Best for: Executive summaries, presentations, highlighting key data
Visual Reference: To see examples of all table styles, visit Microsoft's Format an Excel Table guide which includes screenshots of each style.
Additional Resources:
- Create and format Excel tables - Official Microsoft documentation
- Excel table overview - Complete guide to table features
# Create two separate tables in the same sheet
tables = [
{
"name": "SalesTable",
"start_row": 1,
"start_col": 0,
"end_row": 10,
"end_col": 3,
"style": "TableStyleMedium9"
},
{
"name": "SummaryTable",
"start_row": 12,
"start_col": 0,
"end_row": 15,
"end_col": 2,
"style": "TableStyleLight16"
}
]
jet.write_sheet_arrow(df.to_arrow(), "multi_tables.xlsx", tables=tables)table = {
"name": "MyTable", # Required: Unique table identifier
"display_name": "My Data", # Optional: User-friendly name
"start_row": 1, # Required: First row (1-indexed)
"start_col": 0, # Required: First column (0-indexed)
"end_row": 100, # Required: Last row
"end_col": 5, # Required: Last column
"style": "TableStyleMedium2", # Optional: Table style name
"show_first_column": False, # Optional: Bold first column (default: False)
"show_last_column": False, # Optional: Bold last column (default: False)
"show_row_stripes": True, # Optional: Alternating rows (default: True)
"show_column_stripes": False # Optional: Alternating columns (default: False)
}Note: Excel tables automatically include:
- Header row with filter dropdowns
- Structured references for formulas
- Automatic formatting and styling
- Sort and filter capabilities
Create professional charts and visualizations directly in your Excel files. Jetxl supports six chart types with extensive customization options including stacked charts, data labels, styling, and advanced formatting.
Jetxl supports the following chart types:
- Column Chart - Vertical bars, ideal for comparing values across categories
- Bar Chart - Horizontal bars, good for comparing items
- Line Chart - Shows trends over time or continuous data
- Pie Chart - Displays proportions of a whole
- Scatter Chart - Shows relationships between two numerical variables
- Area Chart - Similar to line chart but with filled areas
import polars as pl
import jetxl as jet
# Create sample data
df = pl.DataFrame({
"Month": ["Jan", "Feb", "Mar", "Apr", "May"],
"Sales": [1000, 1500, 1200, 1800, 2000],
"Costs": [800, 900, 850, 1000, 1100]
})
# Define a column chart
charts = [{
"chart_type": "column",
"start_row": 1, # Data starts at row 1 (header)
"start_col": 0, # First column (Month)
"end_row": 5, # Last data row
"end_col": 2, # Last column (Costs)
"from_col": 4, # Chart position: start column
"from_row": 1, # Chart position: start row
"to_col": 12, # Chart position: end column
"to_row": 15, # Chart position: end row
"title": "Monthly Sales and Costs",
"category_col": 0, # Use first column (Month) for X-axis
"show_legend": True,
"x_axis_title": "Month",
"y_axis_title": "Amount ($)"
}]
jet.write_sheet_arrow(
df.to_arrow(),
"chart_example.xlsx",
charts=charts
)Every chart requires these basic parameters:
chart = {
# Required: Chart type
"chart_type": "column", # column, bar, line, pie, scatter, area
# Required: Data range (1-indexed for rows, 0-indexed for columns)
# Option 1: Individual parameters
"start_row": 1, # First data row (including header)
"start_col": 0, # First data column
"end_row": 10, # Last data row
"end_col": 3, # Last data column
# Option 2: Tuple format (alternative to above)
"data_range": (0, 0, 9, 3), # (start_row, start_col, end_row, end_col)
# Required: Chart position on worksheet
"from_col": 5, # Starting column for chart
"from_row": 1, # Starting row for chart
"to_col": 15, # Ending column for chart
"to_row": 20, # Ending row for chart
# Optional: Chart customization
"title": "My Chart", # Chart title
"category_col": 0, # Column to use for category axis (X-axis)
"series_names": ["Series 1", "Series 2"], # Custom series names
"show_legend": True, # Show/hide legend
"legend_position": "right", # Legend position: "right", "left", "top", "bottom", "none"
"x_axis_title": "Categories", # X-axis label
"y_axis_title": "Values", # Y-axis label
# Optional: Advanced styling
"stacked": True, # Stack series (column, bar, line, area)
"percent_stacked": True, # Stack as 100% (column, bar, line, area)
"show_data_labels": True, # Show data labels on chart
"chart_style": 104, # Excel chart style (1-48)
# Optional: Axis scaling
"axis_min": 0.0, # Minimum Y-axis value
"axis_max": 100.0, # Maximum Y-axis value
# Optional: Title styling
"title_bold": True, # Bold title text
"title_font_size": 1800, # Title font size (hundredths of point, e.g., 1800 = 18pt)
"title_color": "FF0000", # Title color (ARGB hex)
# Optional: Axis title styling
"axis_title_bold": True, # Bold axis title text
"axis_title_font_size": 1200, # Axis title font size (hundredths of point)
"axis_title_color": "00B050", # Axis title color (ARGB hex)
# Optional: Legend styling
"legend_bold": True, # Bold legend text
"legend_font_size": 1000, # Legend font size (hundredths of point)
}Create stacked charts to show composition or percent stacked to show proportions:
import polars as pl
import jetxl as jet
df = pl.DataFrame({
"Quarter": ["Q1", "Q2", "Q3", "Q4"],
"Revenue": [25000, 28000, 31000, 35000],
"Profit": [5000, 6500, 7200, 8500],
"Expenses": [20000, 21500, 23800, 26500]
})
# Regular stacked column chart
stacked_chart = [{
"chart_type": "column",
"data_range": (0, 0, 3, 3),
"from_col": 5,
"from_row": 0,
"to_col": 15,
"to_row": 20,
"title": "Stacked Revenue Components",
"category_col": 0,
"series_names": ["Revenue", "Profit", "Expenses"],
"stacked": True, # Stack the series
"show_data_labels": True,
"x_axis_title": "Quarter",
"y_axis_title": "Amount ($)"
}]
# Percent stacked column chart
percent_chart = [{
"chart_type": "column",
"data_range": (0, 0, 3, 3),
"from_col": 5,
"from_row": 22,
"to_col": 15,
"to_row": 42,
"title": "Percentage Distribution",
"category_col": 0,
"series_names": ["Revenue", "Profit", "Expenses"],
"percent_stacked": True, # Stack as 100%
"show_data_labels": True,
"x_axis_title": "Quarter",
"y_axis_title": "Percentage"
}]
jet.write_sheet_arrow(
df.to_arrow(),
"stacked_charts.xlsx",
charts=[*stacked_chart, *percent_chart]
)Note: stacked and percent_stacked work with column, bar, line, and area charts.
Excel provides 48 pre-defined chart styles that apply coordinated colors, effects, and formatting. Each chart type interprets these styles differently.
Chart Style Numbers (1-48):
Chart styles are organized into categories:
- 1-10: Colorful variations with different color schemes
- 11-16: Monochrome styles (black, white, gray variations)
- 17-32: Colorful outlined styles with borders
- 33-40: Soft color palettes
- 41-48: Modern gradient and flat design styles
Popular Styles by Use Case:
# Professional/Corporate
"chart_style": 2 # Blue colorful
"chart_style": 11 # Monochrome gray
"chart_style": 26 # Dark professional
# Modern/Vibrant
"chart_style": 42 # Gradient modern
"chart_style": 102 # Contemporary (if available)
"chart_style": 104 # Bright modern
# Print-Friendly
"chart_style": 11 # Black and white
"chart_style": 15 # High contrastHow to Find Your Preferred Style:
The best way to discover chart styles is to test them in Excel:
- Create a chart in Excel manually
- Click the chart and go to Chart Design β Chart Styles
- Preview different styles in the gallery
- Note the style you like - styles are numbered in the gallery order
- Use that number in Jetxl's
chart_styleparameter
Example - Testing Multiple Styles:
# Create the same chart with different styles to compare
for style_num in [2, 11, 26, 42, 104]:
charts = [{
"chart_type": "column",
"data_range": (0, 0, 3, 2),
"from_col": 5,
"from_row": 0,
"to_col": 15,
"to_row": 20,
"title": f"Chart Style {style_num}",
"chart_style": style_num,
"category_col": 0
}]
jet.write_sheet_arrow(df.to_arrow(), f"style_{style_num}.xlsx", charts=charts)Chart Style Reference:
- Excel Chart Styles Gallery - Microsoft's official guide
- Styles are part of the Office Open XML standard
- Different Excel versions may render styles slightly differently
- Not all style numbers work with all chart types
Font sizes in charts use Excel's internal unit system based on the Office Open XML (OOXML) standard.
Font Size Format:
- Values are in hundredths of a point
- 1 point = 100 units
- Standard Excel points = units Γ· 100
Common Font Size Conversions:
# Title Sizes
"title_font_size": 800 # 8pt - Small title
"title_font_size": 1000 # 10pt - Compact title
"title_font_size": 1200 # 12pt - Standard title
"title_font_size": 1400 # 14pt - Medium title
"title_font_size": 1600 # 16pt - Large title
"title_font_size": 1800 # 18pt - Extra large title
"title_font_size": 2400 # 24pt - Presentation title
"title_font_size": 3200 # 32pt - Header/banner
# Axis Title Sizes
"axis_title_font_size": 900 # 9pt - Small
"axis_title_font_size": 1000 # 10pt - Standard
"axis_title_font_size": 1100 # 11pt - Medium
"axis_title_font_size": 1200 # 12pt - Large
# Legend Sizes
"legend_font_size": 800 # 8pt - Compact
"legend_font_size": 900 # 9pt - Small
"legend_font_size": 1000 # 10pt - Standard
"legend_font_size": 1100 # 11pt - MediumWhy Hundredths of a Point?
- Precise control over text sizing
- Matches Excel's internal OOXML format
- Allows fractional point sizes (e.g., 1050 = 10.5pt)
- Same system used throughout Microsoft Office
Quick Conversion Formula:
# Points to hundredths
hundredths = points * 100
# Hundredths to points
points = hundredths / 100
# Example: 14pt title
title_font_size = 14 * 100 # = 1400Control the Y-axis range to focus on relevant data ranges or maintain consistent scales across charts.
Basic Axis Scaling:
charts = [{
"chart_type": "line",
"data_range": (0, 0, 3, 1),
"from_col": 5, "from_row": 0,
"to_col": 15, "to_row": 20,
"title": "Test Scores",
"category_col": 0,
"axis_min": 0.0, # Y-axis starts at 0
"axis_max": 100.0, # Y-axis ends at 100
}]When to Use Axis Scaling:
- Percentage Data (0-100%):
"axis_min": 0.0,
"axis_max": 100.0, # Perfect for showing 0-100% range- Normalized Data (0-1):
"axis_min": 0.0,
"axis_max": 1.0, # For decimal percentages (use with percent_stacked)- Focus on Variance:
# Data ranges from 85-95, default would show 0-100
"axis_min": 80.0,
"axis_max": 100.0, # Zoom in to show meaningful differences- Compare Multiple Charts:
# Use same scale across all charts for fair comparison
"axis_min": 0.0,
"axis_max": 50000.0, # All revenue charts use same scale- Symmetric Ranges:
# For profit/loss or change data
"axis_min": -10000.0,
"axis_max": 10000.0, # Symmetric around zeroSpecial Case - Percent Stacked Charts:
# When using percent_stacked, values are 0.0 to 1.0
charts = [{
"chart_type": "column",
"data_range": (0, 0, 3, 3),
"percent_stacked": True,
"axis_min": 0.0, # 0% = 0.0
"axis_max": 1.0, # 100% = 1.0
"y_axis_title": "Percentage"
}]Axis Scaling Best Practices:
- Always start at 0.0 for bar/column charts to avoid misleading visuals
- Use axis_min > 0 only for line charts showing trends
- For percent_stacked, always use axis_min=0.0 and axis_max=1.0
- Omit axis_min/axis_max to let Excel auto-scale
- Use consistent scales when comparing multiple charts
Customize chart appearance with styling options:
import polars as pl
import jetxl as jet
df = pl.DataFrame({
"Month": ["Jan", "Feb", "Mar", "Apr"],
"Sales": [10000, 12000, 11000, 13000],
"Target": [9000, 10000, 12000, 14000]
})
charts = [{
"chart_type": "bar",
"data_range": (0, 0, 3, 2),
"from_col": 5,
"from_row": 0,
"to_col": 15,
"to_row": 20,
# Title styling
"title": "Sales vs Target",
"title_bold": True,
"title_font_size": 1800, # 18pt (in hundredths of point)
"title_color": "0070C0", # Blue (ARGB hex without alpha)
# Axis titles with styling
"x_axis_title": "Amount ($)",
"y_axis_title": "Month",
"axis_title_bold": True,
"axis_title_font_size": 1200, # 12pt
"axis_title_color": "00B050", # Green
# Legend styling
"show_legend": True,
"legend_position": "bottom",
"legend_bold": True,
"legend_font_size": 1000, # 10pt
# Chart style and data labels
"chart_style": 104, # Apply Excel chart style
"show_data_labels": True,
# Axis scaling
"axis_min": 0.0,
"axis_max": 15000.0,
"category_col": 0
}]
jet.write_sheet_arrow(df.to_arrow(), "styled_chart.xlsx", charts=charts)Chart Style Numbers:
- Excel supports chart styles numbered 1-48
- Each chart type has different style variations
- Common styles: 2 (colorful), 11 (monochrome), 26 (dark), 42 (gradient)
- Experiment with different numbers to find your preferred style
Font Sizes:
- Font sizes are specified in hundredths of a point
- Examples: 800 = 8pt, 1000 = 10pt, 1200 = 12pt, 1800 = 18pt
Add data labels to show values directly on chart elements:
df = pl.DataFrame({
"Product": ["A", "B", "C", "D"],
"Sales": [250, 380, 420, 290]
})
charts = [{
"chart_type": "column",
"data_range": (0, 0, 3, 1),
"from_col": 3,
"from_row": 0,
"to_col": 11,
"to_row": 15,
"title": "Product Sales",
"category_col": 0,
"show_data_labels": True, # Display values on bars
"x_axis_title": "Product",
"y_axis_title": "Units Sold"
}]
jet.write_sheet_arrow(df.to_arrow(), "chart_with_labels.xlsx", charts=charts)Control the Y-axis range for better visualization:
df = pl.DataFrame({
"Month": ["Jan", "Feb", "Mar", "Apr"],
"Score": [92, 88, 95, 91]
})
charts = [{
"chart_type": "line",
"data_range": (0, 0, 3, 1),
"from_col": 3,
"from_row": 0,
"to_col": 11,
"to_row": 15,
"title": "Test Scores",
"category_col": 0,
"axis_min": 80.0, # Start Y-axis at 80
"axis_max": 100.0, # End Y-axis at 100
"x_axis_title": "Month",
"y_axis_title": "Score",
"show_data_labels": True
}]
jet.write_sheet_arrow(df.to_arrow(), "scaled_chart.xlsx", charts=charts)df = pl.DataFrame({
"Category": ["North", "South", "East", "West"],
"Sales": [2500, 1800, 2200, 1500]
})
charts = [{
"chart_type": "pie",
"data_range": (0, 0, 3, 1),
"from_col": 3,
"from_row": 0,
"to_col": 10,
"to_row": 15,
"title": "Regional Distribution",
"title_bold": True,
"title_font_size": 1800,
"category_col": 0,
"show_data_labels": True, # Show percentages/values
"legend_font_size": 1100
}]
jet.write_sheet_arrow(df.to_arrow(), "pie_chart.xlsx", charts=charts)df = pl.DataFrame({
"X": [1, 2, 3, 4, 5],
"Y1": [10, 25, 30, 45, 60],
"Y2": [15, 20, 35, 40, 55]
})
charts = [{
"chart_type": "scatter",
"data_range": (0, 0, 4, 2),
"from_col": 5,
"from_row": 0,
"to_col": 15,
"to_row": 20,
"title": "Correlation Analysis",
"axis_min": 0.0,
"axis_max": 70.0,
"x_axis_title": "X Values",
"y_axis_title": "Y Values",
"show_data_labels": False,
"series_names": ["Series 1", "Series 2"]
}]
jet.write_sheet_arrow(df.to_arrow(), "scatter_chart.xlsx", charts=charts)You can add multiple charts to the same worksheet:
df = pl.DataFrame({
"Month": ["Jan", "Feb", "Mar", "Apr"],
"Revenue": [10000, 12000, 11000, 13000],
"Expenses": [7000, 8000, 7500, 8500],
"Profit": [3000, 4000, 3500, 4500]
})
charts = [
{
# Column chart for Revenue and Expenses
"chart_type": "column",
"data_range": (0, 0, 3, 2),
"from_col": 5,
"from_row": 0,
"to_col": 13,
"to_row": 15,
"title": "Revenue & Expenses",
"title_bold": True,
"category_col": 0,
"series_names": ["Revenue", "Expenses"],
"show_data_labels": True,
"x_axis_title": "Month",
"y_axis_title": "Amount ($)"
},
{
# Line chart for Profit trend
"chart_type": "line",
"data_range": (0, 0, 3, 3),
"from_col": 5,
"from_row": 17,
"to_col": 13,
"to_row": 32,
"title": "Profit Trend",
"title_color": "00B050",
"category_col": 0,
"series_names": ["Profit"],
"x_axis_title": "Month",
"y_axis_title": "Profit ($)",
"chart_style": 26
}
]
jet.write_sheet_arrow(df.to_arrow(), "multiple_charts.xlsx", charts=charts)Here's a comprehensive example using all available chart features:
import polars as pl
import jetxl as jet
df = pl.DataFrame({
"Quarter": ["Q1", "Q2", "Q3", "Q4"],
"Revenue": [25000, 28000, 31000, 35000],
"Profit": [5000, 6500, 7200, 8500],
"Expenses": [20000, 21500, 23800, 26500]
})
charts = [{
"chart_type": "column",
# Data range - use tuple or individual parameters
"data_range": (0, 0, 3, 3),
# OR: "start_row": 1, "start_col": 0, "end_row": 4, "end_col": 3,
# Chart position
"from_col": 5,
"from_row": 0,
"to_col": 15,
"to_row": 20,
# Basic settings
"title": "Quarterly Financial Performance",
"category_col": 0,
"series_names": ["Revenue", "Profit", "Expenses"],
# Stacking
"percent_stacked": True, # Show as percentages
# Title styling
"title_bold": True,
"title_font_size": 1600,
"title_color": "0070C0",
# Axis configuration
"x_axis_title": "Quarter",
"y_axis_title": "Percentage",
"axis_title_bold": True,
"axis_title_font_size": 1200,
"axis_title_color": "00B050",
"axis_min": 0.0,
"axis_max": 1.0,
# Legend
"show_legend": True,
"legend_position": "bottom",
"legend_bold": True,
"legend_font_size": 1000,
# Visual enhancements
"show_data_labels": True,
"chart_style": 102
}]
jet.write_sheet_arrow(
df.to_arrow(),
"complete_chart.xlsx",
charts=charts
)Add images (logos, charts, diagrams) to your Excel sheets with precise positioning control.
| Format | Extensions | Best For | Notes |
|---|---|---|---|
| PNG | .png |
Logos, screenshots | Lossless, supports transparency |
| JPEG | .jpg, .jpeg |
Photos | Smaller file size, no transparency |
| GIF | .gif |
Simple graphics | Limited colors, supports animation |
| BMP | .bmp |
Windows bitmaps | Large file size, uncompressed |
| TIFF | .tiff, .tif |
High-quality images | Professional printing |
The simplest way to add images is from file paths:
import polars as pl
import jetxl as jet
df = pl.DataFrame({
"Product": ["Widget A", "Widget B", "Widget C"],
"Sales": [1000, 1500, 1200]
})
images = [{
"path": "company_logo.png",
"from_col": 0, # Column A (0-based)
"from_row": 0, # Row 1 (0-based)
"to_col": 2, # Column C
"to_row": 5 # Row 6
}]
jet.write_sheet_arrow(
df.to_arrow(),
"report_with_logo.xlsx",
images=images
)Load images from memory (useful for API responses, databases, or generated images):
import requests
import jetxl as jet
# Download image from URL
response = requests.get("https://example.com/chart.png")
image_bytes = response.content
# Or read from file
with open("logo.png", "rb") as f:
image_bytes = f.read()
images = [{
"data": image_bytes,
"extension": "png", # Required when using bytes
"from_col": 5,
"from_row": 1,
"to_col": 12,
"to_row": 15
}]
jet.write_sheet_arrow(
df.to_arrow(),
"report.xlsx",
images=images
)Add multiple images to the same sheet:
images = [
{
# Company logo in top-left
"path": "company_logo.png",
"from_col": 0,
"from_row": 0,
"to_col": 2,
"to_row": 4
},
{
# Product image on the right
"path": "product_photo.jpg",
"from_col": 8,
"from_row": 2,
"to_col": 12,
"to_row": 10
},
{
# Chart at the bottom
"path": "sales_chart.png",
"from_col": 0,
"from_row": 15,
"to_col": 10,
"to_row": 30
}
]
jet.write_sheet_arrow(
df.to_arrow(),
"multi_image_report.xlsx",
images=images
)Images are positioned using Excel's column/row coordinates:
- Columns are 0-indexed: A=0, B=1, C=2, etc.
- Rows are 0-indexed: 0=row 1, 1=row 2, etc.
# Position image from B3 to F10
image = {
"path": "image.png",
"from_col": 1, # Column B (0-based)
"from_row": 2, # Row 3 (0-based)
"to_col": 5, # Column F
"to_row": 9 # Row 10
}Size Recommendations:
- Small: 2-4 columns Γ 5-8 rows (logos, icons)
- Medium: 4-6 columns Γ 8-12 rows (product photos)
- Large: 6-10 columns Γ 12-20 rows (charts, diagrams)
Create professional reports with logos, data, and visualizations:
import polars as pl
import jetxl as jet
# Sample data
df = pl.DataFrame({
"Month": ["Jan", "Feb", "Mar", "Apr"],
"Revenue": [10000, 12000, 11000, 13000],
"Costs": [7000, 8000, 7500, 8500]
})
# Add company logo, data table, and chart image
jet.write_sheet_arrow(
df.to_arrow(),
"monthly_report.xlsx",
sheet_name="Financial Report",
styled_headers=True,
freeze_rows=1,
column_formats={
"Revenue": "currency",
"Costs": "currency"
},
images=[
{
# Logo at top
"path": "company_logo.png",
"from_col": 0,
"from_row": 0,
"to_col": 2,
"to_row": 3
},
{
# Visualization chart
"path": "revenue_chart.png",
"from_col": 5,
"from_row": 5,
"to_col": 15,
"to_row": 25
}
]
)Combine all visualization features:
df = pl.DataFrame({
"Product": ["A", "B", "C", "D"],
"Q1": [100, 150, 120, 180],
"Q2": [110, 160, 130, 190],
"Q3": [120, 170, 140, 200]
})
jet.write_sheet_arrow(
df.to_arrow(),
"complete_dashboard.xlsx",
tables=[{
"name": "SalesTable",
"start_row": 1,
"start_col": 0,
"end_row": 4,
"end_col": 3,
"style": "TableStyleMedium2"
}],
charts=[{
"chart_type": "column",
"start_row": 1,
"start_col": 0,
"end_row": 4,
"end_col": 3,
"from_col": 5,
"from_row": 5,
"to_col": 13,
"to_row": 20,
"title": "Quarterly Sales"
}],
images=[{
"path": "company_logo.png",
"from_col": 0,
"from_row": 0,
"to_col": 2,
"to_row": 3
}]
)Each sheet can have its own images:
df_summary = pl.DataFrame({"Metric": ["Total Sales"], "Value": [50000]})
df_details = pl.DataFrame({"Product": ["A", "B"], "Sales": [30000, 20000]})
sheets = [
{
"data": df_summary.to_arrow(),
"name": "Summary",
"images": [{
"path": "company_logo.png",
"from_col": 0,
"from_row": 0,
"to_col": 2,
"to_row": 4
}]
},
{
"data": df_details.to_arrow(),
"name": "Details",
"images": [{
"path": "product_breakdown.png",
"from_col": 4,
"from_row": 1,
"to_col": 12,
"to_row": 15
}]
}
]
jet.write_sheets_arrow(sheets, "multi_sheet_report.xlsx", num_threads=2)Combine with image generation libraries:
import matplotlib.pyplot as plt
import io
import jetxl as jet
# Generate a chart with matplotlib
fig, ax = plt.subplots()
ax.plot([1, 2, 3, 4], [10, 20, 15, 25])
ax.set_title("Sales Trend")
# Save to bytes
img_buffer = io.BytesIO()
fig.savefig(img_buffer, format='png', dpi=150, bbox_inches='tight')
img_bytes = img_buffer.getvalue()
plt.close(fig)
# Add to Excel
jet.write_sheet_arrow(
df.to_arrow(),
"report_with_chart.xlsx",
images=[{
"data": img_bytes,
"extension": "png",
"from_col": 5,
"from_row": 1,
"to_col": 15,
"to_row": 20
}]
)-
File Formats
- Use PNG for logos and screenshots (lossless, supports transparency)
- Use JPEG for photos (smaller file size)
- Use GIF for simple animations (limited color palette)
-
Image Size
- Optimize images before embedding to reduce file size
- Use appropriate dimensions for your target (don't embed 4K images for small displays)
- Consider using PIL/Pillow to resize images programmatically
-
Performance
- Large images increase Excel file size
- Multiple large images can slow down Excel opening time
- Compress images before embedding when possible
-
Positioning
- Leave space around images for readability
- Align images with data columns when possible
- Use consistent sizing for professional appearance
hyperlinks = [
(2, 0, "https://example.com", "Visit Example"), # Row 2, Col 0
(3, 0, "https://google.com", None), # Display URL as text
(4, 2, "mailto:user@example.com", "Email Us")
]
jet.write_sheet_arrow(df.to_arrow(), "links.xlsx", hyperlinks=hyperlinks)formulas = [
(2, 3, "=SUM(A2:C2)", None), # Simple formula
(5, 3, "=AVERAGE(D2:D4)", "45.5"), # Formula with cached value
(6, 3, "=IF(D5>50,\"High\",\"Low\")", None)
]
jet.write_sheet_arrow(df.to_arrow(), "formulas.xlsx", formulas=formulas)The cached value is the pre-calculated result shown before Excel recalculates the formula:
formulas = [
# No cached value - Excel calculates on open
(2, 3, "=SUM(A2:C2)", None),
# With cached value - shows "45.5" until Excel recalculates
(5, 3, "=AVERAGE(D2:D4)", "45.5"),
]When to use cached values:
- Formulas that reference external data sources
- Complex calculations that take time to compute
- When you want to show a result before Excel opens
- Cross-workbook references that may not be available
When to use None:
- Simple formulas (SUM, AVERAGE of local cells)
- When you want Excel to always calculate fresh
- Formulas with volatile functions (NOW, RAND)
merge_cells = [
(1, 0, 1, 3), # Merge A1:D1 (start_row, start_col, end_row, end_col)
(2, 0, 5, 0), # Merge A2:A5
]
jet.write_sheet_arrow(df.to_arrow(), "merged.xlsx", merge_cells=merge_cells)validations = [{
"start_row": 2,
"start_col": 0,
"end_row": 100,
"end_col": 0,
"type": "list",
"items": ["Option A", "Option B", "Option C"],
"show_dropdown": True,
"error_title": "Invalid Selection",
"error_message": "Please select from the dropdown"
}]
jet.write_sheet_arrow(df.to_arrow(), "validation.xlsx", data_validations=validations)validations = [{
"start_row": 2,
"start_col": 1,
"end_row": 100,
"end_col": 1,
"type": "whole_number",
"min": 1,
"max": 100,
"error_title": "Out of Range",
"error_message": "Value must be between 1 and 100"
}]validations = [{
"start_row": 2,
"start_col": 2,
"end_row": 100,
"end_col": 2,
"type": "decimal",
"min": 0.0,
"max": 100.0
}]Validate text input length:
validations = [{
"start_row": 2,
"start_col": 0,
"end_row": 100,
"end_col": 0,
"type": "text_length",
"min": 3,
"max": 20,
"error_title": "Invalid Username",
"error_message": "Username must be 3-20 characters long"
}]
jet.write_sheet_arrow(df.to_arrow(), "validation.xlsx", data_validations=validations)conditional_formats = [{
"start_row": 2,
"start_col": 2,
"end_row": 100,
"end_col": 2,
"rule_type": "cell_value",
"operator": "greater_than", # less_than, equal, not_equal, etc.
"value": "50",
"priority": 1,
"style": {
"font": {
"bold": True,
"color": "FFFF0000" # Red text
},
"fill": {
"pattern": "solid",
"fg_color": "FFFFFF00" # Yellow background
}
}
}]
jet.write_sheet_arrow(df.to_arrow(), "conditional.xlsx", conditional_formats=conditional_formats)The cell_value rule type supports these operators:
# Greater than
"operator": "greater_than", "value": "100"
# Less than
"operator": "less_than", "value": "50"
# Equal to
"operator": "equal", "value": "0"
# Not equal to
"operator": "not_equal", "value": "0"
# Greater than or equal
"operator": "greater_than_or_equal", "value": "100"
# Less than or equal
"operator": "less_than_or_equal", "value": "50"
# Between (use comma-separated values)
"operator": "between", "value": "10,100"# Two-color scale (min to max)
conditional_formats = [{
"start_row": 2,
"start_col": 2,
"end_row": 100,
"end_col": 2,
"rule_type": "color_scale",
"min_color": "FFF8696B", # Red
"max_color": "FF63BE7B", # Green
"priority": 1
}]
# Three-color scale (min to mid to max)
# Better for showing deviation from average/target
conditional_formats = [{
"start_row": 2,
"start_col": 2,
"end_row": 100,
"end_col": 2,
"rule_type": "color_scale",
"min_color": "FFF8696B", # Red for low values
"mid_color": "FFFFEB84", # Yellow for medium values
"max_color": "FF63BE7B", # Green for high values
"priority": 1
}]Highlight the highest or lowest values in a range:
# Highlight top 10 values
conditional_formats = [{
"start_row": 2,
"start_col": 2,
"end_row": 100,
"end_col": 2,
"rule_type": "top10",
"rank": 10,
"bottom": False, # Top 10 (set to True for bottom 10)
"priority": 1,
"style": {
"font": {"bold": True, "color": "FF00B050"},
"fill": {"pattern": "solid", "fg_color": "FFC6EFCE"}
}
}]
# Highlight bottom 5 values
conditional_formats = [{
"start_row": 2,
"start_col": 2,
"end_row": 100,
"end_col": 2,
"rule_type": "top10",
"rank": 5,
"bottom": True, # Bottom 5
"priority": 1,
"style": {
"font": {"bold": True, "color": "FFFF0000"},
"fill": {"pattern": "solid", "fg_color": "FFFFC7CE"}
}
}]Create multi-sheet workbooks with full independent formatting per sheet. Each sheet supports all features from write_sheet_arrow() including tables, charts, images, conditional formatting, data validation, formulas, cell styles, and more.
import polars as pl
import jetxl as jet
df_sales = pl.DataFrame({"Product": ["A", "B"], "Revenue": [100, 200]})
df_costs = pl.DataFrame({"Product": ["A", "B"], "Cost": [50, 80]})
df_profit = pl.DataFrame({"Product": ["A", "B"], "Profit": [50, 120]})
sheets = [
{
"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
}
]
jet.write_sheets_arrow(
sheets,
"report.xlsx",
num_threads=4 # Use 4 threads for parallel generation
)Each sheet can have completely different formatting:
sheets = [
{
"data": df_sales.to_arrow(),
"name": "Sales",
"styled_headers": True,
"auto_filter": True,
"freeze_rows": 1,
"column_formats": {
"Date": "date",
"Revenue": "currency",
"Tax": "percentage"
},
"tables": [{
"name": "SalesTable",
"start_row": 1,
"start_col": 0,
"end_row": 100,
"end_col": 5,
"style": "TableStyleMedium2"
}],
"tab_color": "FF00B050" # Green tab
},
{
"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": "FFFF0000",
"show_value": True,
"priority": 1
}],
"tab_color": "FFFF0000" # Red tab
},
{
"data": df_profit.to_arrow(),
"name": "Profit",
"write_header_row": False, # Data only, no headers
"hidden_columns": [2, 3],
"gridlines_visible": False,
"zoom_scale": 150
}
]
jet.write_sheets_arrow(sheets, "advanced.xlsx", num_threads=3)sheets = [
{
"data": df_monthly.to_arrow(),
"name": "Monthly Sales",
"styled_headers": True,
"freeze_rows": 1,
# Excel table
"tables": [{
"name": "MonthlySales",
"start_row": 1,
"start_col": 0,
"end_row": 12,
"end_col": 3,
"style": "TableStyleMedium9"
}],
# Chart
"charts": [{
"chart_type": "column",
"start_row": 1,
"start_col": 0,
"end_row": 12,
"end_col": 2,
"from_col": 5,
"from_row": 1,
"to_col": 13,
"to_row": 16,
"title": "Monthly Sales Trend",
"category_col": 0,
"x_axis_title": "Month",
"y_axis_title": "Revenue ($)"
}],
# Logo
"images": [{
"path": "company_logo.png",
"from_col": 0,
"from_row": 0,
"to_col": 2,
"to_row": 4
}]
},
{
"data": df_quarterly.to_arrow(),
"name": "Quarterly",
"auto_filter": True,
# Different chart type
"charts": [{
"chart_type": "pie",
"start_row": 1,
"start_col": 0,
"end_row": 4,
"end_col": 1,
"from_col": 3,
"from_row": 1,
"to_col": 10,
"to_row": 15,
"title": "Market Share"
}]
}
]
jet.write_sheets_arrow(sheets, "dashboard.xlsx", num_threads=2)Every sheet supports the full API from write_sheet_arrow():
sheets = [
{
"data": df.to_arrow(),
"name": "Complete Example",
# Basic formatting
"auto_filter": True,
"freeze_rows": 1,
"freeze_cols": 0,
"auto_width": True,
"styled_headers": True,
"write_header_row": True,
# Column formatting
"column_widths": {"Name": 25.0, "Email": "200px", "Notes": "auto"},
"column_formats": {"Date": "date", "Amount": "currency", "Rate": "percentage"},
# Cell operations
"merge_cells": [(1, 0, 1, 3), (5, 0, 8, 0)],
"row_heights": {1: 30.0, 5: 25.0},
# Cell styles
"cell_styles": [{
"row": 2,
"col": 0,
"font": {"bold": True, "color": "FFFF0000", "size": 14.0},
"fill": {"pattern": "solid", "fg_color": "FFFFFF00"},
"alignment": {"horizontal": "center", "vertical": "center"}
}],
# Data validation
"data_validations": [{
"start_row": 2, "start_col": 4,
"end_row": 100, "end_col": 4,
"type": "list",
"items": ["Active", "Pending", "Closed"],
"show_dropdown": True
}],
# Hyperlinks
"hyperlinks": [(2, 0, "https://example.com", "Visit Site")],
# Formulas
"formulas": [(5, 5, "=SUM(A2:A4)", None)],
# Conditional formatting
"conditional_formats": [{
"start_row": 2, "start_col": 3,
"end_row": 100, "end_col": 3,
"rule_type": "color_scale",
"min_color": "FFF8696B",
"mid_color": "FFFFEB84",
"max_color": "FF63BE7B",
"priority": 1
}],
# Excel tables
"tables": [{
"name": "DataTable",
"start_row": 1, "start_col": 0,
"end_row": 100, "end_col": 5,
"style": "TableStyleMedium2"
}],
# Charts
"charts": [{
"chart_type": "column",
"start_row": 1, "start_col": 0,
"end_row": 12, "end_col": 2,
"from_col": 7, "from_row": 1,
"to_col": 15, "to_row": 18,
"title": "Sales Chart"
}],
# Images
"images": [{
"path": "logo.png",
"from_col": 0, "from_row": 0,
"to_col": 2, "to_row": 4
}],
# Appearance
"gridlines_visible": False,
"zoom_scale": 120,
"tab_color": "FF4472C4",
"default_row_height": 18.0,
"hidden_columns": [2],
"hidden_rows": [5, 6],
"right_to_left": False,
"data_start_row": 0
}
]
jet.write_sheets_arrow(sheets, "everything.xlsx", num_threads=1)Performance Notes:
- XML generation is fully parallel across
num_threads - Each sheet can have independent formatting with minimal overhead (<1%)
- Style registry is shared for deduplication
- Recommended:
num_threads = min(cpu_count, len(sheets))
Control worksheet visibility settings:
import polars as pl
import jetxl as jet
df = pl.DataFrame({
"Product": ["A", "B", "C"],
"Price": [10.0, 20.0, 30.0]
})
# Hide gridlines and set zoom
jet.write_sheet_arrow(
df.to_arrow(),
"clean_view.xlsx",
gridlines_visible=False, # Hide gridlines for cleaner look
zoom_scale=150 # Zoom to 150% (range: 10-400)
)Color-code your sheets for better organization:
# Single sheet with colored tab
jet.write_sheet_arrow(
df.to_arrow(),
"colored_tab.xlsx",
tab_color="FFFF0000" # Red tab (ARGB format)
)
# Multiple sheets with different colors
sheets = [
{
"data": df_sales.to_arrow(),
"name": "Sales",
"tab_color": "FF00B050" # Green
},
{
"data": df_costs.to_arrow(),
"name": "Costs",
"tab_color": "FFFF0000" # Red
},
{
"data": df_profit.to_arrow(),
"name": "Profit",
"tab_color": "FF0070C0" # Blue
}
]
jet.write_sheets_arrow(sheets, "colored_tabs.xlsx", num_threads=2)Common Tab Colors:
"FF4472C4"- Blue"FF00B050"- Green"FFFF0000"- Red"FFFFC000"- Orange"FF7030A0"- Purple
Set a consistent row height for all rows:
jet.write_sheet_arrow(
df.to_arrow(),
"tall_rows.xlsx",
default_row_height=25.0, # 25 points (default is 15)
row_heights={
1: 35.0, # Override: make header taller
5: 20.0 # Override: specific row
}
)Hidden Rows and Columns
Hide sensitive or intermediate data:
df = pl.DataFrame({
"ID": [1, 2, 3],
"Name": ["Alice", "Bob", "Charlie"],
"Secret": ["X", "Y", "Z"],
"Salary": [50000, 60000, 75000],
"Bonus": [5000, 6000, 7500]
})
jet.write_sheet_arrow(
df.to_arrow(),
"hidden_data.xlsx",
hidden_columns=[2, 4], # Hide "Secret" (col 2) and "Bonus" (col 4)
hidden_rows=[3] # Hide row 3
)Note: Hidden data is still in the file - it's just not visible by default. Users can unhide it in Excel.
For languages like Arabic, Hebrew, Persian, etc.:
df = pl.DataFrame({
"Χ©Χ": ["ΧΧΧΧ‘", "ΧΧΧ", "Χ¦'Χ¨ΧΧ"],
"ΧΧΧ": [25, 30, 35]
})
jet.write_sheet_arrow(
df.to_arrow(),
"hebrew.xlsx",
right_to_left=True # Sheet flows from right to left
)When your Excel file has multiple header rows, dummy rows, or template rows, exclude them from width calculation:
# Scenario: Your file structure is:
# Row 1: Company logo (merged cells with long text)
# Row 2: Report title "Q4 2024 Financial Summary - Confidential"
# Row 3: Date range
# Row 4: Empty spacing row
# Row 5: Column headers (Name, Amount, Status)
# Row 6+: Actual data
# Without data_start_row, auto_width uses ALL rows including dummy rows
# This makes columns unnecessarily wide to fit the title text
jet.write_sheet_arrow(
df.to_arrow(),
"complex_report.xlsx",
auto_width=True,
data_start_row=5 # Start width calculation from row 5 (actual data)
# Now columns are sized based on data + headers only
)Common use cases:
- Reports with title rows, logos, or metadata at the top
- Templates with pre-existing formatting rows
- Multi-section reports where only one section should determine width
- Files with merged header rows that contain long text
Write arbitrary content above your DataFrame data - perfect for report titles, metadata, logos in merged cells, or template headers:
import polars as pl
import jetxl as jet
df = pl.DataFrame({
"Name": ["Alice", "Bob"],
"Sales": [1000, 1500]
})
# Add title rows, metadata, spacing before DataFrame
jet.write_sheet_arrow(
df.to_arrow(),
"report.xlsx",
header_content=[
(1, 0, "ACME Corporation"), # Row 1, Col A
(1, 2, "Confidential"), # Row 1, Col C
(2, 0, "Q4 2024 Sales Report"), # Row 2, Col A
(3, 0, "Generated: 2024-10-17"), # Row 3, Col A
# Row 4 is empty (spacing)
],
data_start_row=5, # DataFrame starts at row 5
write_header_row=True, # Row 5 will have column headers
# Actual data starts at row 6
merge_cells=[
(1, 0, 1, 1), # Merge A1:B1 for company name
]
)Common use cases:
- Report headers with company name, logo placeholder, dates
- Multi-line titles with merged cells
- Metadata rows (author, generated date, version)
- Template text that shouldn't come from DataFrame
- Section dividers in complex reports
Coordinates:
- Row numbers are 1-based (row 1 is first row)
- Column numbers are 0-based (0=A, 1=B, 2=C, etc.)
header_contentrows are written BEFORE DataFrame data- Use
data_start_rowto position DataFrame below header content
Combine appearance settings for a polished look:
import polars as pl
import jetxl as jet
df = pl.DataFrame({
"Quarter": ["Q1", "Q2", "Q3", "Q4"],
"Revenue": [100000, 120000, 115000, 140000],
"Target": [95000, 110000, 120000, 135000]
})
jet.write_sheet_arrow(
df.to_arrow(),
"executive_dashboard.xlsx",
sheet_name="Performance",
# Clean appearance
gridlines_visible=False,
zoom_scale=120,
tab_color="FF0070C0",
default_row_height=20.0,
# Formatting
styled_headers=True,
freeze_rows=1,
auto_width=True,
column_formats={
"Revenue": "currency",
"Target": "currency"
},
# Visualization
charts=[{
"chart_type": "column",
"start_row": 1,
"start_col": 0,
"end_row": 4,
"end_col": 2,
"from_col": 4,
"from_row": 1,
"to_col": 12,
"to_row": 18,
"title": "Revenue vs Target",
"category_col": 0,
"x_axis_title": "Quarter",
"y_axis_title": "Amount ($)"
}]
)Here's a comprehensive example using multiple features:
import polars as pl
import jetxl as jet
# Create sample data
df = pl.DataFrame({
"Date": ["2024-01-01", "2024-01-02", "2024-01-03"],
"Product": ["Widget A", "Widget B", "Widget C"],
"Quantity": [100, 150, 75],
"Price": [19.99, 29.99, 39.99],
"Revenue": [1999.0, 4498.5, 2999.25]
})
# Tables auto-size to data
tables = [{
"name": "SalesData",
"display_name": "Q1 Sales",
"start_row": 1,
"start_col": 0,
"end_row": 0, # Auto-calculate from DataFrame rows
"end_col": 0, # Auto-calculate from DataFrame columns
"style": "TableStyleMedium9",
"show_row_stripes": True
}]
# Add conditional formatting
conditional_formats = [{
"start_row": 2,
"start_col": 4,
"end_row": 4,
"end_col": 4,
"rule_type": "data_bar",
"color": "FF638EC6",
"show_value": True,
"priority": 1
}]
# Add chart
charts = [{
"chart_type": "column",
"start_row": 1,
"start_col": 0,
"end_row": 4,
"end_col": 4,
"from_col": 6,
"from_row": 1,
"to_col": 14,
"to_row": 18,
"title": "Revenue by Product",
"category_col": 1, # Product column
"x_axis_title": "Product",
"y_axis_title": "Revenue ($)",
"show_legend": False
}]
# Add logo image
images = [{
"path": "company_logo.png",
"from_col": 0,
"from_row": 0,
"to_col": 2,
"to_row": 3
}]
# Write to Excel
jet.write_sheet_arrow(
df.to_arrow(),
"sales_report.xlsx",
sheet_name="Q1 Sales",
styled_headers=True,
freeze_rows=1,
auto_width=True,
column_formats={
"Date": "date",
"Price": "currency",
"Revenue": "currency"
},
tables=tables,
conditional_formats=conditional_formats,
charts=charts,
images=images
)Jetxl achieves its performance through several key optimizations:
- Zero-copy Arrow Integration: Direct memory access to DataFrame buffers without copying
- SIMD XML Escaping: Hardware-accelerated string processing
- Pre-calculated Buffer Sizing: Single allocation per sheet with exact size calculation
- Parallel Sheet Generation: Multi-threaded XML generation for multiple sheets
- Optimized Number Formatting: Fast integer/float detection and conversion
- Streaming Compression: On-the-fly ZIP compression with minimal memory overhead
import polars as pl
import jetxl as jet
# For very large datasets, use batched reading
df = pl.scan_csv("huge_file.csv").collect()
# Jetxl handles large datasets efficiently
jet.write_sheet_arrow(
df.to_arrow(),
"large_output.xlsx",
auto_width=False # Disable auto-width for faster generation
)def create_report_style():
return {
"styled_headers": True,
"auto_filter": True,
"freeze_rows": 1,
"column_formats": {
"Date": "date",
"Amount": "currency",
"Percentage": "percentage"
}
}
# Apply consistent styling across reports
jet.write_sheet_arrow(df.to_arrow(), "report.xlsx", **create_report_style())try:
jet.write_sheet_arrow(df.to_arrow(), "output.xlsx")
except IOError as e:
print(f"Failed to write file: {e}")
except ValueError as e:
print(f"Invalid data: {e}")- β 5x faster (1M rows: 2.06s vs 10.05s)
- β Near-zero Python memory overhead
- β Zero-copy DataFrame integration
- β Multi-threaded sheet generation
- β Modern Python API with type hints
- β Larger output files (less aggressive compression)
- β Fewer advanced chart customizations
- β 27x faster (1M rows: 2.06s vs 56.25s)
- β Dramatically lower memory usage
- β Native Arrow/Polars/Pandas support
- β Write-only (openpyxl supports reading)
- β Fewer cell-level features
- β 20x faster (1M rows: 2.06s vs 40.85s)
- β 2000x lower memory (~0 MB vs 2.1 GB at 1M rows)
- β More formatting options (conditional formatting, tables, charts)
- β Multi-sheet threading support
- β Requires
.to_arrow()conversion
- β 27-40x faster depending on engine
- β Direct Polars support (no pandas dependency)
- β Richer formatting options
- β Multi-threading support
- β Dramatically lower memory footprint
- β 5.5x faster (1M rows: 2.06s vs 11.27s)
- β Native Arrow support (no data conversion needed)
- β More formatting options
- β Multi-threaded sheet generation
- Numeric:
Int8/16/32/64,UInt8/16/32/64,Float32/64 - String:
Utf8,LargeUtf8 - Boolean:
Bool - Temporal:
Date32/64,Timestamp(all units),Time32/64
str,int,float,bool,datetime,None
Excel Tables
- Format an Excel Table - Complete guide with visual examples of all table styles
- Overview of Excel Tables - Features and capabilities
- Using Structured References - Advanced table formulas
Excel Charts
- Available Chart Types in Office - Complete reference for all chart types
- Create a Chart from Start to Finish - Step-by-step guide
Number Formats
- Excel Number Format Codes - Complete reference for custom format codes
- Available Number Formats - Built-in format options
- Custom Number Format Guidelines - Creating custom formats
- Create Custom Number Formats - Detailed tutorial
Understanding Excel Colors
- Excel uses ARGB format for colors:
AARRGGBBwhere:AA= Alpha channel (transparency) - usuallyFFfor fully opaqueRR= Red component (00-FF in hexadecimal)GG= Green component (00-FF in hexadecimal)BB= Blue component (00-FF in hexadecimal)
Example Colors:
"FFFF0000" # Red (FF = opaque, FF0000 = red)
"FF00FF00" # Green (FF = opaque, 00FF00 = green)
"FF0000FF" # Blue (FF = opaque, 0000FF = blue)
"FFFFFF00" # Yellow (red + green)
"FFFF00FF" # Magenta (red + blue)
"FF00FFFF" # Cyan (green + blue)
"FF000000" # Black
"FFFFFFFF" # WhiteCommon Conditional Formatting Colors:
# Red-Yellow-Green color scale (default Excel)
"FFF8696B" # Red for low values
"FFFFEB84" # Yellow for middle values
"FF63BE7B" # Green for high values
# Data bar colors
"FF638EC6" # Blue (Excel default data bar)
"FF5687C5" # Dark blue
"FFFF6347" # Tomato redColor Picker Tools:
- RapidTables RGB Color Picker - Interactive color selection with RGB/hex codes
- W3Schools Color Picker - Simple online color chooser
- Microsoft RGB Function - Excel's RGB function documentation
Tutorials & Guides
- ExcelJet Custom Number Formats - Comprehensive formatting guide
- Ablebits Excel Tables Guide - Advanced table formatting
- W3Schools Excel Tutorial - Beginner-friendly Excel basics
Made with β€οΈ and π¦ by the Jetxl team