Skip to main content

How to Create Custom UI Renderings

Flyte UI renderings allow you to visualize complex data types directly within the Flyte console. By implementing the Renderable protocol or using built-in renderers, you can transform raw Python objects into rich HTML reports (Decks) that improve the observability of your workflows.

Use Built-in Renderers

flyte-sdk provides several built-in renderers in flyte.types._renderer for common data types. While pandas.DataFrame and pyarrow.Table renderers are registered by default, others like MarkdownRenderer and SourceCodeRenderer can be used manually to log content to a task's report.

Render Markdown and Source Code

To render markdown or syntax-highlighted Python code, instantiate the corresponding renderer and pass the output to flyte.report.log.

from flyte.report import log
from flyte.types._renderer import MarkdownRenderer, SourceCodeRenderer

@task
def my_task():
# Render Markdown
md_text = "# Analysis Results\n\n- Accuracy: 95%\n- Precision: 92%"
log(MarkdownRenderer().to_html(md_text))

# Render Source Code with syntax highlighting
code = "def hello():\n print('world')"
log(SourceCodeRenderer(title="My Function").to_html(code))

Automatic DataFrame Rendering

For pandas.DataFrame and pyarrow.Table, flyte-sdk automatically generates HTML tables in the UI. This is handled by TopFrameRenderer and ArrowRenderer respectively, which are registered in flyte.io._dataframe.

If you want to customize the number of rows or columns displayed for a pandas DataFrame, you can re-register the TopFrameRenderer with specific limits:

import pandas as pd
from flyte.io._dataframe import DataFrameTransformerEngine
from flyte.types._renderer import TopFrameRenderer

# Customize to show only the first 5 rows and 5 columns
DataFrameTransformerEngine.register_renderer(
pd.DataFrame,
TopFrameRenderer(max_rows=5, max_cols=5)
)

Create a Custom Renderer

You can create a custom renderer for any Python type by implementing the Renderable protocol defined in flyte.types._renderer. The protocol requires a to_html method that returns a valid HTML string.

1. Implement the Renderable Protocol

Define a class that takes your custom type and returns an HTML representation.

from typing import Any
from flyte.types._renderer import Renderable

class MyCustomRenderer:
def to_html(self, python_value: Any) -> str:
# python_value is the object returned by your task
return f"<div style='color: blue;'>Custom Value: {python_value}</div>"

# Verify implementation against the protocol
assert isinstance(MyCustomRenderer(), Renderable)

2. Register the Renderer

To make the renderer work automatically for a specific type returned by a task, register it with the DataFrameTransformerEngine.

from flyte.io._dataframe import DataFrameTransformerEngine

class MyData:
def __init__(self, content: str):
self.content = content

# Register the renderer for the MyData type
DataFrameTransformerEngine.register_renderer(MyData, MyCustomRenderer())

@task
def get_data() -> MyData:
return MyData("Hello Flyte")

When get_data executes, the Flyte engine will detect the MyData return type, find the registered MyCustomRenderer, and include the generated HTML in the task's UI report.

Log Dependencies

The PythonDependencyRenderer is a specialized renderer that captures the current environment's installed packages. This is useful for debugging environment mismatches.

from flyte.report import log
from flyte.types._renderer import PythonDependencyRenderer

@task
def debug_env():
# Generates an HTML table of pip list and a hidden requirements.txt
renderer = PythonDependencyRenderer()
log(renderer.to_html())

Troubleshooting

Private Module Imports

The renderers are located in flyte.types._renderer. While this is a private module, these classes are the intended way to interface with the Flyte Deck rendering system. Ensure you import them directly from flyte.types._renderer.

HTML Safety

When using flyte.report.log, the Report class in flyte.report._report does not escape the HTML content. It is the responsibility of the to_html method in your Renderable implementation to ensure the HTML is safe and correctly formatted.

Manual Logging vs. Automatic Rendering

  • Automatic: Works for types registered via DataFrameTransformerEngine.register_renderer. The UI report is generated automatically when the task returns that type.
  • Manual: Use flyte.report.log(renderer.to_html(value)) for types that are not registered or when you want to log multiple items to the same report tab.