Write a Custom Component for Langflow
08 Dec 2025Langflow ships with a large set of built-in components, but the moment you need to call an internal API with a specific auth scheme, or apply business logic that doesn’t map to an existing node, you need a custom component. It’s a Python class with a defined interface — not a plugin system with a steep learning curve.
The shape of a custom component
Every custom component is a Python class inheriting from Component, with inputs, outputs, and a method that does the actual work. Here’s a minimal one that calls an internal API and returns the result as text:
from langflow.custom import Component
from langflow.io import StringInput, SecretStrInput, Output
from langflow.schema.message import Message
import requests
class InternalAPILookup(Component):
display_name = "Internal API Lookup"
description = "Queries an internal service by ID and returns the response."
icon = "search"
inputs = [
StringInput(
name="record_id",
display_name="Record ID",
required=True,
),
SecretStrInput(
name="api_key",
display_name="API Key",
required=True,
),
]
outputs = [
Output(display_name="Result", name="result", method="run_lookup"),
]
def run_lookup(self) -> Message:
response = requests.get(
f"https://internal.example.com/api/records/{self.record_id}",
headers={"Authorization": f"Bearer {self.api_key}"},
timeout=10,
)
response.raise_for_status()
data = response.json()
return Message(text=data.get("summary", ""))
A few things here matter more than they look:
SecretStrInput instead of StringInput for the API key. This masks it in the UI and keeps it out of exported flow JSON in plaintext — using a regular string input for a secret means it shows up in flow exports for anyone who has the file.
timeout=10 on the request. Without an explicit timeout, a hung internal service blocks the flow indefinitely instead of failing after a reasonable wait — the default requests behavior is to wait forever, which is almost never what you want in a pipeline that other steps depend on completing.
response.raise_for_status() turns an HTTP error into a Python exception Langflow surfaces properly in the UI, instead of quietly continuing with a failed response’s data — silent failures here look identical to successful empty results downstream.
Where the file goes
Drop it in your Langflow custom components directory. If you’re running Langflow via Docker, mount it in:
docker run -v $(pwd)/custom_components:/app/custom_components langflowai/langflow
Set the environment variable so Langflow knows to load from it:
LANGFLOW_COMPONENTS_PATH=/app/custom_components
Restart Langflow. Your component shows up in the component sidebar under its display_name, ready to drag into a flow like any built-in node.
Testing it without the UI
Before wiring it into a full flow, instantiate and call it directly — this catches import errors and logic bugs faster than clicking through the UI on every change:
component = InternalAPILookup(record_id="abc123", api_key="test-key")
result = component.run_lookup()
print(result.text)
Common mistakes
Forgetting the method argument on Output. Langflow needs to know which method produces that output — omit it and the component loads but produces no result when run, with no obvious error pointing at the cause.
Returning a raw Python type instead of a Langflow schema object. Returning a plain string instead of Message(text=...) breaks downstream components expecting the standard message shape — some accept raw strings gracefully, others don’t, and the failure shows up several nodes later, not at the component itself.
Not handling the case where the API returns nothing useful. data.get("summary", "") returns an empty string rather than crashing on a missing key — decide deliberately whether an empty result should be a valid pass-through or something that should raise and stop the flow. Silently passing through empty results into a prompt template downstream produces a confidently wrong LLM answer with no error anywhere in the chain.
Once you have one custom component working, adding more follows the same pattern — inputs, outputs, one method. The interface is small on purpose; most of the actual work is whatever Python logic sits inside run_lookup.