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.