How to Display Images in Langfuse Traces Output
When you’re tracing AI operations with Langfuse, you often want to include images in your outputs and have them displayed in the Langfuse interface.
Here’s our final result:
The Problem
You have an image (maybe an annotated screenshot, a chart, or processed data visualization) that you want to show in your Langfuse trace output.
If you add it as base64 it will show as an attachment which you then need to click to see. And that opens in a separate tab.

We don’t want that.
The Solution
Upload your image to external storage (like Backblaze B2, AWS S3, or any cloud storage), get the public URL, then use markdown format as your Langfuse output.
# Simple markdown string as output
output = f""
PythonStep-by-Step Implementation
1. Set Up Your Storage
I’m using Backblaze B2. Check out this tutorial to set it up.
import os
import b2sdk.v2 as b2
from dotenv import load_dotenv
from uuid import uuid4
load_dotenv()
B2_KEY_ID = os.getenv("B2_KEY_ID")
B2_APP_KEY = os.getenv("B2_APPLICATION_KEY")
BUCKET_NAME = "your-bucket-name"
Python2. Create Upload Function
# Global variables for lazy initialization
_b2_api = None
_b2_bucket = None
def upload_image_to_b2(image_bytes: bytes, filename: str) -> str:
"""Upload image bytes and return public URL"""
global _b2_api, _b2_bucket
# Initialize B2 API once
if _b2_api is None:
info = b2.InMemoryAccountInfo()
_b2_api = b2.B2Api(info)
_b2_api.authorize_account("production", B2_KEY_ID, B2_APP_KEY)
_b2_bucket = _b2_api.get_bucket_by_name(BUCKET_NAME)
# Upload and get public URL
_b2_bucket.upload_bytes(image_bytes, filename)
return _b2_api.get_download_url_for_file_name(BUCKET_NAME, filename)
Python3. Use in Langfuse Traces
from langfuse.decorators import langfuse_context, observe
import requests
@observe()
def process_image():
# Download a demo image (or use your existing image)
response = requests.get("https://picsum.photos/400/300")
image_bytes = response.content
# Upload to storage with unique filename
filename = f"demo_image_{uuid4().hex[:8]}.jpg"
image_url = upload_image_to_b2(image_bytes, filename)
# Create markdown output
markdown_output = f""
# Force Langfuse to show this as output (NOT JSON)
langfuse_context.update_current_observation(output=markdown_output)
return markdown_output
PythonKey Points
1. Output Must Be Non-JSON
Don’t do this:
# WRONG - JSON format won't render image
output = {"image": f""}
PythonDo this:
# RIGHT - Direct markdown string
output = f""
Python2. Force the Output
Use update_current_observation()
to set exactly what you want to display:
3. Make URLs Public
Ensure your storage bucket/files are publicly accessible, or Langfuse won’t be able to display them.
Complete Working Example
import os
import b2sdk.v2 as b2
import requests
from uuid import uuid4
from langfuse.decorators import langfuse_context, observe
from dotenv import load_dotenv
load_dotenv()
# B2 setup
_b2_api = None
_b2_bucket = None
def upload_to_b2(data: bytes, filename: str) -> str:
global _b2_api, _b2_bucket
if _b2_api is None:
info = b2.InMemoryAccountInfo()
_b2_api = b2.B2Api(info)
_b2_api.authorize_account("production",
os.getenv("B2_KEY_ID"),
os.getenv("B2_APPLICATION_KEY"))
_b2_bucket = _b2_api.get_bucket_by_name("your-bucket-name")
_b2_bucket.upload_bytes(data, filename)
return _b2_api.get_download_url_for_file_name("your-bucket-name", filename)
@observe()
def demo_image_processing():
# Get a demo image
response = requests.get("https://picsum.photos/400/300")
image_bytes = response.content
# Upload to B2
filename = f"demo_image_{uuid4().hex[:8]}.jpg"
public_url = upload_to_b2(image_bytes, filename)
# Create markdown output for Langfuse
markdown_output = f""
# Force this to be the output (replaces any LLM response)
langfuse_context.update_current_observation(output=markdown_output)
return markdown_output
# Run the demo
result = demo_image_processing()
print(f"Langfuse will show: {result}")
PythonWhat You Get
In Langfuse, instead of seeing JSON or raw text, you’ll see your actual image rendered inline. The image is stored on your infrastructure, and Langfuse displays it via the public URL.
This approach works for any visual content: charts, screenshots, processed images, or generated graphics. Just upload to storage, get the URL, format as markdown, and force it as your Langfuse output.