Files
langflow/docs/versioned_docs/version-1.10.0/Components/components-custom-components.mdx
Mendon Kissling 2411d8036e docs: build OpenAPI spec and cut version 1.10 (#13537)
* build-api

* bump-version-to-1.10

* fix-broken-links
2026-06-08 18:25:14 +00:00

599 lines
23 KiB
Plaintext

---
title: Create custom Python components
slug: /components-custom-components
---
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:
* The Python class that inherits from `Component`.
* Class-level attributes that identify and describe the component.
* [Input and output lists](#inputs-and-outputs) that determine data flow.
* Methods that define the component's behavior and logic.
* Internal variables for [Error handling and logging](#error-handling-and-logging)
Use the [Custom component quickstart](#quickstart) to add an example component to Langflow, and then use the reference guide that follows for more advanced component customization.
## Custom component quickstart {#quickstart}
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.
### Create a Python file
<PartialBasicComponentStructure />
### Save the custom component {#custom-component-path}
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](/environment-variables), 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
```
:::warning Directory Depth Limitation
Components can be at most 2 levels deep.
For example, `data/custom_component.py` is discoverable, but `data/tools/custom_component.py` is not discoverable.
To increase the depth limit, modify `MAX_DEPTH` in `src/lfx/src/lfx/custom/directory_reader/directory_reader.py`. Subdirectories will still appear as separate categories, not nested hierarchies.
:::
### Create the `__init__.py` file
Each 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.
```python
from .dataframe_processor import DataFrameProcessor
__all__ = ["DataFrameProcessor"]
```
<details closed>
<summary>Lazy load the DataFrameProcessor component</summary>
Alternatively, you can load your component **lazily**, which is better for performance but a little more complex.
```python
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](https://github.com/langflow-ai/langflow/blob/main/src/lfx/src/lfx/components/FAISS/__init__.py).
</details>
### Load your component
Ensure the application builds your component.
1. To rebuild the backend and frontend, run `make install_frontend && make build_frontend && make install_backend && uv run langflow run --port 7860`.
2. 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.
### Docker deployment
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.
```bash
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](#custom-component-path).
```
/app/custom_components/ # LANGFLOW_COMPONENTS_PATH
└── data/
├── __init__.py
└── dataframe_processor.py
```
## How components execute
Langflow's engine manages:
1. **Instantiation**: A component is created and internal structures are initialized.
2. **Assigning Inputs**: Values from the visual editor or connections are assigned to component fields.
3. **Validation and Setup**: Optional hooks like `_pre_run_setup`.
4. **Outputs Generation**: `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:
```python
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:
```python
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.
```python
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
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
Inputs are defined in a class-level `inputs` list. When Langflow loads the component, it uses this list to render component fields and [ports](/concepts-components#component-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`).
```python
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](https://github.com/langflow-ai/langflow/blob/main/src/lfx/src/lfx/inputs/input_mixin.py) in the Langflow codebase.
For a list of all available input types, see the [input type definitions](https://github.com/langflow-ai/langflow/blob/main/src/lfx/src/lfx/inputs/inputs.py) in the Langflow codebase.
```python
from lfx.io import StrInput, DataInput, MultilineInput, IntInput, BoolInput, DropdownInput, FileInput, CodeInput, ModelInput, HandleInput, Output
```
### Outputs
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`:
```python
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](https://github.com/langflow-ai/langflow/blob/main/src/lfx/src/lfx/template/field/base.py) in the Langflow codebase. Common parameters include:
**Additional return types:**
* **`Message`**: Structured chat messages
* **`JSON`**: Flexible object with `.data` and optional `.text`
* **`Table`**: Tabular data (pandas DataFrame subclass)
* **Primitive types**: `str`, `int`, `bool`, not recommended for type consistency
#### Associated methods
Each 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.
```python
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})
```
#### Components with multiple outputs
A component can define multiple outputs.
Each output can have a different corresponding method.
For example:
```python
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.
<Tabs>
<TabItem value="false" label="False or not set" default>
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.
```python
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",
),
]
```
</TabItem>
<TabItem value="true" label="True">
In this example, all outputs are available simultaneously in the visual editor.
```python
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,
),
]
```
</TabItem>
</Tabs>
### Tool mode
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`:
```python
inputs = [
MessageTextInput(
name="message",
display_name="Mensage",
info="Enter the message that will be processed directly by the tool",
tool_mode=True,
),
]
```
## Typed annotations
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.
### Common return types
<Tabs>
<TabItem value="message" label="Message" default>
For chat-style outputs. Connects to any of several `Message`-compatible inputs.
```python
def produce_message(self) -> Message:
return Message(text="Hello! from typed method!", sender="System")
```
</TabItem>
<TabItem value="data" label="Data">
For structured data like dicts or partial texts. Connects only to `DataInput` (ports that accept `JSON`).
```python
def get_processed_data(self) -> Data:
processed = {"key1": "value1", "key2": 123}
return Data(data=processed)
```
</TabItem>
<TabItem value="dataframe" label="DataFrame">
For tabular data. Connects only to `DataFrameInput` (ports that accept `Table`).
```python
def build_df(self) -> DataFrame:
pdf = pd.DataFrame({"A": [1, 2], "B": [3, 4]})
return DataFrame(pdf)
```
</TabItem>
<TabItem value="primitives" label="Primitive Types">
Returning primitives is allowed, but wrapping in `JSON` or `Message` is recommended for better consistency in the visual editor.
```python
def compute_sum(self) -> int:
return sum(self.numbers)
```
</TabItem>
</Tabs>
## Enable dynamic fields
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`.
```python
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,
),
]
```
### Show or hide fields based on user selections
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.
```python
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
```python
if field_value == "regex":
build_config["regex_pattern"]["required"] = True
else:
build_config["regex_pattern"]["required"] = False
```
* **`advanced`**: Move fields to the "Advanced" section
```python
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
```python
if field_value == "regex":
build_config["operator"]["options"] = ["regex", "contains", "starts_with"]
else:
build_config["operator"]["options"] = ["equals", "contains", "not_equals"]
```
## Error handling and logging
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.
```python
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.
```python
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.
```python
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.
```python
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](https://www.structlog.org) for application-level logging that outputs to `langflow.log` files. For more information, see [Logs](/logging).
This example logs a message when the component starts processing a file.
```python
def process_file(self, file_path: str):
self.log(f"Processing file {file_path}")
```
## Contribute custom components to Langflow
To contribute your custom component to the Langflow project, see [Contribute components](/contributing-components).