docs/versioned_docs/version-1.9.0/Components/components-custom-components.mdx
import Icon from "@site/src/components/icon"; import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import PartialBasicComponentStructure from '../_partial-basic-component-structure.mdx';
Create your own custom components to add any functionality you need to Langflow, from API integrations to data processing.
In Langflow's node-based environment, each node is a "component" that performs discrete functions. Custom components in Langflow are built upon:
Component.Use the Custom component quickstart to add an example component to Langflow, and then use the reference guide that follows for more advanced component customization.
Create a custom DataFrameProcessor component by creating a Python file, saving it in the correct folder, including an __init__.py file, and loading it into Langflow.
Save the custom component in the Langflow directory where the UI will discover and load it.
By default, Langflow looks for custom components in the src/lfx/src/lfx/components directory.
When saving components in the default directory, components must be organized in a specific directory structure to be properly loaded and displayed in the visual editor.
Components must be placed inside category folders, not directly in the base directory.
The category folder name determines where the component appears in the Langflow <Icon name="Component" aria-hidden="true" /> Core components menu.
For example, to add the example DataFrameProcessor component to the Data category, place it in the data subfolder:
src/lfx/src/lfx/components/
└── data/ # Category folder (determines menu location)
├── __init__.py # Required - makes it a Python package
└── dataframe_processor.py # Your custom component file
If you're creating custom components in a different location using the LANGFLOW_COMPONENTS_PATH environment variable, components must be similarly organized in a specific directory structure to be displayed in the visual editor.
/your/custom/components/path/ # Base directory set by LANGFLOW_COMPONENTS_PATH
└── category_name/
├── __init__.py
└── custom_component.py
You can have multiple category folders to organize components into different categories, with multiple components inside each folder:
/app/custom_components/
├── data/
│ ├── __init__.py
│ ├── custom_component.py
│ └── dataframe_processor.py
└── tools/
├── __init__.py
└── custom_tool.py
__init__.py fileEach category directory must contain an __init__.py file for Langflow to properly recognize and load the components.
This is a Python package requirement that ensures the directory is treated as a module.
To include the DataFrameProcessor component, create a file named __init__.py in your component's directory with the following content.
from .dataframe_processor import DataFrameProcessor
__all__ = ["DataFrameProcessor"]
Alternatively, you can load your component lazily, which is better for performance but a little more complex.
from __future__ import annotations
from typing import TYPE_CHECKING, Any
from lfx.components._importing import import_mod
if TYPE_CHECKING:
from lfx.components.data.dataframe_processor import DataFrameProcessor
_dynamic_imports = {
"DataFrameProcessor": "dataframe_processor",
}
__all__ = [
"DataFrameProcessor",
]
def __getattr__(attr_name: str) -> Any:
"""Lazily import data components on attribute access."""
if attr_name not in _dynamic_imports:
msg = f"module '{__name__}' has no attribute '{attr_name}'"
raise AttributeError(msg)
try:
result = import_mod(attr_name, _dynamic_imports[attr_name], __spec__.parent)
except (ModuleNotFoundError, ImportError, AttributeError) as e:
msg = f"Could not import '{attr_name}' from '{__name__}': {e}"
raise AttributeError(msg) from e
globals()[attr_name] = result
return result
def __dir__() -> list[str]:
return list(__all__)
For an additional example of lazy loading, see the FAISS component.
</details>Ensure the application builds your component.
To rebuild the backend and frontend, run make install_frontend && make build_frontend && make install_backend && uv run langflow run --port 7860.
Refresh the frontend application.
Your new DataFrameProcessor component is available in the <Icon name="Component" aria-hidden="true" /> Core components menu under the Data category in the visual editor.
When running Langflow in Docker, mount your custom components directory and set the LANGFLOW_COMPONENTS_PATH environment variable in the docker run command to point to the custom components directory.
docker run -d \
--name langflow \
-p 7860:7860 \
-v ./custom_components:/app/custom_components \
-e LANGFLOW_COMPONENTS_PATH=/app/custom_components \
langflowai/langflow:latest
Create the same custom components directory structure as the example in Save the custom component.
/app/custom_components/ # LANGFLOW_COMPONENTS_PATH
└── data/
├── __init__.py
└── dataframe_processor.py
Langflow's engine manages:
_pre_run_setup.run() or build_results() triggers output methods.You can customize execution by overriding these optional hooks in your custom component code.
_pre_run_setup() - Used during Validation and Setup.
Add this method inside your component class to initialize component state before execution begins:
class MyComponent(Component):
# ... your inputs, outputs, and other attributes ...
def _pre_run_setup(self):
if not hasattr(self, "_initialized"):
self._initialized = True
self.iteration = 0
Override run or _run - Used during Outputs Generation.
Add this method inside your component class to customize the main execution logic:
class MyComponent(Component):
async def_run(self):
# Custom execution logic here
# This runs instead of the default output method calls
pass
Store data in self.ctx.
Use self.ctx in any of your component methods to share data between method calls.
class MyComponent(Component):
def _pre_run_setup(self):
# Initialize counter in setup
self.ctx["processed_items"] = 0
def process_data(self) -> Data:
# Increment counter during processing
self.ctx["processed_items"] += 1
return Data(data={"item": f"processed {self.ctx['processed_items']}"})
def get_summary(self) -> Data:
# Access counter in different method
total = self.ctx["processed_items"]
return Data(data={"summary": f"Processed {total} items total"})
Inputs and outputs are class-level configurations that define how data flows through the component, how it appears in the visual editor, and how connections to other components are validated.
Inputs are defined in a class-level inputs list. When Langflow loads the component, it uses this list to render component fields and ports in the visual editor. Users or other components provide values or connections to fill these inputs.
An input is usually an instance of a class from lfx.io (such as StrInput, DataInput, or MessageTextInput).
For example, this component has three inputs: a text field (StrInput), a Boolean toggle (BoolInput), and a dropdown selection (DropdownInput).
from lfx.io import StrInput, BoolInput, DropdownInput
inputs = [
StrInput(name="title", display_name="Title"),
BoolInput(name="enabled", display_name="Enabled", value=True),
DropdownInput(name="mode", display_name="Mode", options=["Fast", "Safe", "Experimental"], value="Safe")
]
The StrInput creates a single-line text field for entering text. The name="title" parameter means you access this value in your component methods with self.title, while display_name="Title" shows "Title" as the label in the visual editor.
The BoolInput creates a boolean toggle that's enabled by default with value=True. Users can turn this on or off, and you access the current state with self.enabled.
The DropdownInput provides a selection menu with three predefined options: "Fast", "Safe", and "Experimental".
The value="Safe" sets "Safe" as the default selection, and you access the user's choice with self.mode.
For a list of all available parameters, see the BaseInputMixin definition in the Langflow codebase.
For a list of all available input types, see the input type definitions in the Langflow codebase.
from lfx.io import StrInput, DataInput, MultilineInput, IntInput, BoolInput, DropdownInput, FileInput, CodeInput, ModelInput, HandleInput, Output
Outputs are defined in a class-level outputs list. When Langflow renders a component, each output becomes a connector point in the visual editor. When you connect something to an output, Langflow automatically calls the corresponding method and passes the returned object to the next component.
An output is usually an instance of Output from lfx.io.
For example, this component has one output that returns a Table:
from lfx.io import Output
from lfx.schema import DataFrame
outputs = [
Output(
name="df_out",
display_name="DataFrame Output",
method="build_df"
)
]
def build_df(self) -> DataFrame:
# Process data and return DataFrame
df = DataFrame({"col1": [1, 2], "col2": [3, 4]})
self.status = f"Built DataFrame with {len(df)} rows."
return df
The Output creates a connector point in the visual editor labeled DataFrame Output. The name="df_out" parameter identifies this output, while display_name="DataFrame Output" shows the label in the UI. The method="build_df" parameter tells Langflow to call the build_df method when this output is connected to another component.
The build_df method processes data and returns a Table. The -> DataFrame type annotation helps Langflow validate connections and provides color-coding in the visual editor. You can also set self.status to show progress messages in the UI.
For a complete list of all available parameters, see the Output class definition in the Langflow codebase. Common parameters include:
Additional return types:
Message: Structured chat messagesJSON: Flexible object with .data and optional .textTable: Tabular data (pandas DataFrame subclass)str, int, bool, not recommended for type consistencyEach output is linked to a method where the output method name must match the method name. The method typically returns objects like Message, JSON, or Table, and can use inputs with self.<input_name>.
For example, the Output defines a connector point called file_contents that will call the read_file method when connected. The read_file method accesses the filename input with self.filename, reads the file content, sets a status message, and returns the content wrapped in a JSON object.
Output(
name="file_contents",
display_name="File Contents",
method="read_file"
)
def read_file(self) -> Data:
path = self.filename
with open(path, "r") as f:
content = f.read()
self.status = f"Read {len(content)} chars from {path}"
return Data(data={"content": content})
A component can define multiple outputs. Each output can have a different corresponding method.
For example:
outputs = [
Output(display_name="Processed Data", name="processed_data", method="process_data"),
Output(display_name="Debug Info", name="debug_info", method="provide_debug_info"),
]
By default, components in Langflow that produce multiple outputs only allow one output selection in the visual editor. The component will have only one output port where the user can select the preferred output type.
This behavior is controlled by the group_outputs parameter:
group_outputs=False (default): When a component has more than one output and group_outputs is false or not set, the outputs are grouped in the visual editor, and the user must select one.
Use this option when the component is expected to return only one type of output when used in a flow.
group_outputs=True: All outputs are available simultaneously in the visual editor. The component has one output port for each output, and the user can connect zero or more outputs to other components.
Use this option when the component is expected to return multiple values that are used in parallel by downstream components or processes.
In this example, the visual editor provides a single output port, and the user can select one of the outputs.
Since group_outputs=False is the default behavior, it doesn't need to be explicitly set in the component, as shown in this example.
outputs = [
Output(
name="structured_output",
display_name="Structured Output",
method="build_structured_output",
),
Output(
name="dataframe_output",
display_name="DataFrame Output",
method="build_structured_dataframe",
),
]
In this example, all outputs are available simultaneously in the visual editor.
outputs = [
Output(
name="true_result",
display_name="True",
method="true_response",
group_outputs=True,
),
Output(
name="false_result",
display_name="False",
method="false_response",
group_outputs=True,
),
]
Components that support Tool Mode can be used as standalone components (when not in Tool Mode) or as tools for other components with a Tools input, such as Agent components.
You can allow a custom component to support Tool Mode by setting tool_mode=True:
inputs = [
MessageTextInput(
name="message",
display_name="Mensage",
info="Enter the message that will be processed directly by the tool",
tool_mode=True,
),
]
In Langflow, typed annotations allow Langflow to visually guide users and maintain flow consistency.
Always annotate your output methods with return types like -> Data, -> Message, or -> DataFrame to enable proper visual editor color-coding and validation.
Use JSON, Message, or Table wrappers instead of returning plain structures for better consistency. Stay consistent with types across your components to make flows predictable and easier to build.
Typed annotations provide color-coding where outputs like -> Data or -> Message get distinct colors, automatic validation that blocks incompatible connections, and improved readability for users to quickly understand data flow between components.
For chat-style outputs. Connects to any of several Message-compatible inputs.
def produce_message(self) -> Message:
return Message(text="Hello! from typed method!", sender="System")
For structured data like dicts or partial texts. Connects only to DataInput (ports that accept JSON).
def get_processed_data(self) -> Data:
processed = {"key1": "value1", "key2": 123}
return Data(data=processed)
For tabular data. Connects only to DataFrameInput (ports that accept Table).
def build_df(self) -> DataFrame:
pdf = pd.DataFrame({"A": [1, 2], "B": [3, 4]})
return DataFrame(pdf)
Returning primitives is allowed, but wrapping in JSON or Message is recommended for better consistency in the visual editor.
def compute_sum(self) -> int:
return sum(self.numbers)
In Langflow, dynamic fields allow inputs to change or appear based on user interactions. You can make an input dynamic by setting dynamic=True. Optionally, setting real_time_refresh=True triggers the update_build_config method to adjust the input's visibility or properties in real time, creating a contextual visual editor experience that only exposes relevant fields based on the user's choices.
In this example, the operator field triggers updates with real_time_refresh=True.
The regex_pattern field is initially hidden and controlled with dynamic=True.
from lfx.custom import Component
from lfx.io import DropdownInput, StrInput
class RegexRouter(Component):
display_name = "Regex Router"
description = "Demonstrates dynamic fields for regex input."
inputs = [
DropdownInput(
name="operator",
display_name="Operator",
options=["equals", "contains", "regex"],
value="equals",
real_time_refresh=True,
),
StrInput(
name="regex_pattern",
display_name="Regex Pattern",
info="Used if operator='regex'",
dynamic=True,
show=False,
),
]
When a user changes a field with real_time_refresh=True, Langflow calls your update_build_config method.
This method lets you show, hide, or modify other fields based on what the user selected.
This example shows the regex_pattern field only when the user selects "regex" from the operator dropdown.
def update_build_config(self, build_config: dict, field_value: str, field_name: str | None = None) -> dict:
if field_name == "operator":
if field_value == "regex":
build_config["regex_pattern"]["show"] = True
else:
build_config["regex_pattern"]["show"] = False
return build_config
You can modify additional field properties in update_build_config other than just show and hide.
required: Make fields required or optional dynamically
if field_value == "regex":
build_config["regex_pattern"]["required"] = True
else:
build_config["regex_pattern"]["required"] = False
advanced: Move fields to the "Advanced" section
if field_value == "experimental":
build_config["regex_pattern"]["advanced"] = False # Show in main section
else:
build_config["regex_pattern"]["advanced"] = True # Hide in advanced
options: Change dropdown options based on other selections
if field_value == "regex":
build_config["operator"]["options"] = ["regex", "contains", "starts_with"]
else:
build_config["operator"]["options"] = ["equals", "contains", "not_equals"]
You can raise standard Python exceptions such as ValueError or specialized exceptions like ToolException when validation fails. Langflow automatically catches these and displays appropriate error messages in the visual editor, helping users quickly identify what went wrong.
def compute_result(self) -> str:
if not self.user_input:
raise ValueError("No input provided.")
# ...
Alternatively, instead of stopping a flow abruptly, you can return a JSON object containing an "error" field. This approach allows the flow to continue operating and enables downstream components to detect and handle the error gracefully.
def run_model(self) -> Data:
try:
# ...
except Exception as e:
return Data(data={"error": str(e)})
Langflow provides several tools to help you debug and manage component execution. You can use self.status to display short messages about execution results directly in the visual editor, making troubleshooting easier for users.
def parse_data(self) -> Data:
# ...
self.status = f"Parsed {len(rows)} rows successfully."
return Data(data={"rows": rows})
You can halt individual output paths when certain conditions fail using self.stop(), without stopping other outputs from the same component.
This example stops the output if the user input is empty, preventing the component from processing invalid data.
def some_output(self) -> Data:
if not self.user_input or len(self.user_input.strip()) == 0:
self.stop("some_output")
return Data(data={"error": "Empty input provided"})
You can log key execution details inside components using self.log(). These logs are stored as structured data and displayed in the "Logs" or "Events" section of the component's detail view, and can be accessed later through the Logs button in the visual editor or exported files.
Component logs are distinct from Langflow's main application logging system. self.log() creates component-specific logs that appear in the UI, while Langflow's main logging system uses structlog for application-level logging that outputs to langflow.log files. For more information, see Logs.
This example logs a message when the component starts processing a file.
def process_file(self, file_path: str):
self.log(f"Processing file {file_path}")
To contribute your custom component to the Langflow project, see Contribute components.