Skip to main content

Data Visualization and HTML Rendering

flyte-sdk provides a flexible rendering system to visualize data directly in the Flyte console. You can either use automatic rendering by annotating your task signatures or manually build multi-tab reports using the flyte.report module.

Automatic Rendering with Annotated Types

You can attach a renderer to a task input or output using typing.Annotated. When Flyte executes the task, it uses the specified renderer to generate an HTML representation for the Flyte Deck.

from typing import Annotated
import pandas as pd
from flyte.types import TopFrameRenderer
import flyte

@flyte.task
def process_data(
# Render the first 5 rows and 5 columns of the input dataframe
df: Annotated[pd.DataFrame, TopFrameRenderer(max_rows=5, max_cols=5)]
) -> pd.DataFrame:
return df

Built-in Renderers

flyte-sdk includes several built-in renderers in flyte.types:

  • TopFrameRenderer(max_rows=10, max_cols=100): Renders a Pandas DataFrame as an HTML table.
  • MarkdownRenderer(): Converts a markdown string into HTML using markdown-it.
  • SourceCodeRenderer(title="Source Code"): Renders Python source code with syntax highlighting using Pygments.
  • PythonDependencyRenderer(title="Dependencies"): Generates a report of installed pip packages and provides a "Copy as requirements.txt" button.

Example using MarkdownRenderer:

from typing import Annotated
from flyte.types import MarkdownRenderer
import flyte

@flyte.task
def get_summary() -> Annotated[str, MarkdownRenderer()]:
return "# Task Summary\n\n* Success: True\n* Items processed: 100"

Creating Custom Renderers

To create a custom renderer, implement the Renderable protocol defined in src/flyte/types/_renderer.py. Your class must provide a to_html method that returns a valid HTML string.

from typing import Annotated, Any
from flyte.types import Renderable
import flyte

class ColorRenderer(Renderable):
def __init__(self, color: str = "blue"):
self._color = color

def to_html(self, python_value: Any) -> str:
# Return an HTML fragment (usually inserted into a <div>)
return f"<div style='color: {self._color}'>Value: {python_value}</div>"

@flyte.task
def custom_render_task(x: int) -> Annotated[int, ColorRenderer(color="red")]:
return x

Manual Reporting with flyte.report

For complex visualizations or multi-tab reports, use the flyte.report module. You must enable reporting in the @task decorator by setting report=True.

import flyte
import flyte.report
import pandas as pd

@flyte.task(report=True)
async def generate_complex_report():
df = pd.DataFrame({"a": [1, 2], "b": [3, 4]})

# Log to the default 'main' tab
flyte.report.log("<h2>Execution Started</h2>")

# Create and log to a specific tab
data_tab = flyte.report.get_tab("Data View")
data_tab.log(df.to_html(classes="table table-striped"))

# Replace content in a tab
status_tab = flyte.report.get_tab("Status")
status_tab.replace("<p>Processing complete!</p>")

# You must flush the report to persist it to storage
await flyte.report.flush.aio()

Key Reporting Functions

  • flyte.report.log(content: str, do_flush: bool = False): Appends HTML content to the "main" tab.
  • flyte.report.replace(content: str, do_flush: bool = False): Overwrites the content of the "main" tab.
  • flyte.report.get_tab(name: str): Returns a Tab object for a specific tab name. Tab objects have their own log(content) and replace(content) methods.
  • flyte.report.flush(): Persists the current report state to Flyte storage. This is available as a synchronous function or an asynchronous one via flush.aio().

Customizing Global Dataframe Rendering

By default, flyte-sdk uses TopFrameRenderer for Pandas DataFrames and ArrowRenderer for PyArrow Tables. You can override these globally using DataFrameTransformerEngine.register_renderer.

from flyte.io._dataframe.dataframe import DataFrameTransformerEngine
from flyte.types import Renderable
import pandas as pd

class MyCustomDFRenderer(Renderable):
def to_html(self, df: pd.DataFrame) -> str:
return f"DataFrame with {len(df)} rows"

# Register the renderer globally for all pd.DataFrame types
DataFrameTransformerEngine.register_renderer(pd.DataFrame, MyCustomDFRenderer())

Troubleshooting and Gotchas

  • HTML Fragments: The to_html method and flyte.report.log should return/accept HTML fragments (e.g., <div>...</div>), not full HTML documents with <html> or <body> tags, as they are typically inserted into existing containers in the Flyte Console.
  • Flushing Reports: If you use flyte.report, the report will not appear in the Flyte Deck unless you call flush() or await flush.aio() before the task completes.
  • Performance: Large dataframes are truncated by default (DEFAULT_MAX_ROWS = 10, DEFAULT_MAX_COLS = 100) to prevent UI performance issues in the browser. Use TopFrameRenderer(max_rows=N) to adjust these limits.