docs: update custom component docs (#10323)

* add-partial

* update-lfx-component-paths

* move-partial

* completed-quickstart

* clean up intro

* try-docker-with-custom-mount

* up-to-typed-annotations

* typed-annotations

* dynamic-fields

* end-of-file

* bundles-naming

* chore: update component index

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
Mendon Kissling
2025-10-28 12:12:53 -04:00
committed by GitHub
parent 28a384cf4f
commit 5e7eb834b3
7 changed files with 477 additions and 488 deletions

View File

@ -6,128 +6,314 @@ 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';
Custom components extend Langflow's functionality through Python classes that inherit from `Component`. This enables integration of new features, data manipulation, external services, and specialized tools.
In Langflow's node-based environment, each node is a "component" that performs discrete functions. Custom components are Python classes which define:
* **Inputs** — Data or parameters your component requires.
* **Outputs** — Data your component provides to downstream nodes.
* **Logic** — How you process inputs to produce outputs.
The benefits of creating custom components include unlimited extensibility, reusability, automatic field generation in the visual editor based on inputs, and type-safe connections between nodes.
Create custom components for performing specialized tasks, calling APIs, or adding advanced logic.
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 that determine data flow.
* Internal variables for logging and advanced logic.
* [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)
## Class-level attributes
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.
Define these attributes to control a custom component's appearance and behavior:
## Custom component quickstart {#quickstart}
```python
class MyCsvReader(Component):
display_name = "CSV Reader"
description = "Reads CSV files"
icon = "file-text"
name = "CSVReader"
documentation = "http://docs.example.com/csv_reader"
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
```
* `display_name`: A user-friendly label shown in the visual editor.
* `description`: A brief summary shown in tooltips and printed below the component name when added to a flow.
* `icon`: A decorative icon from Langflow's icon library, printed next to the name.
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.
Langflow uses [Lucide](https://lucide.dev/icons) for icons. To assign an icon to your component, set the icon attribute to the name of a Lucide icon as a string, such as `icon = "file-text"`. Langflow renders icons from the Lucide library automatically.
```
/your/custom/components/path/ # Base directory set by LANGFLOW_COMPONENTS_PATH
└── category_name/
├── __init__.py
└── custom_component.py
```
* `name`: A unique internal identifier, typically the same name as the folder containing your component code.
* `documentation`: An optional link to external documentation, such as API or product documentation.
You can have multiple category folders to organize components into different categories:
```
/app/custom_components/
├── data/
│ ├── __init__.py
│ └── dataframe_processor.py
└── tools/
├── __init__.py
└── custom_tool.py
```
### Structure of a custom component
### Create the `__init__.py` file
A Langflow custom component is more than a class with inputs and outputs. It includes an internal structure with optional lifecycle steps, output generation, front-end interaction, and logic organization.
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.
A basic component:
* Inherits from `langflow.custom.Component`.
* Declares metadata like `display_name`, `description`, `icon`, and more.
* Defines `inputs` and `outputs` lists.
* Implements methods matching output specifications.
A minimal custom component skeleton contains the following:
To include the `DataFrameProcessor` component, create a file named `__init__.py` in your component's directory with the following content.
```python
from langflow.custom import Component
from langflow.template import Output
from .dataframe_processor import DataFrameProcessor
class MyComponent(Component):
display_name = "My Component"
description = "A short summary."
icon = "sparkles"
name = "MyComponent"
inputs = []
outputs = []
def some_output_method(self):
return ...
__all__ = ["DataFrameProcessor"]
```
### Internal Lifecycle and Execution Flow
<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:
* **Instantiation**: A component is created and internal structures are initialized.
* **Assigning Inputs**: Values from the visual editor or connections are assigned to component fields.
* **Validation and Setup**: Optional hooks like `_pre_run_setup`.
* **Outputs Generation**: `run()` or `build_results()` triggers output methods.
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.
**Optional Hooks**:
You can customize execution by overriding these optional hooks in your custom component code.
* `initialize_data` or `_pre_run_setup` can run setup logic before the component's main execution.
* `__call__`, `run()`, or `_run()` can be overridden to customize how the component is called or to define custom execution logic.
* **`_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 ...
### Inputs and outputs
def _pre_run_setup(self):
if not hasattr(self, "_initialized"):
self._initialized = True
self.iteration = 0
```
Custom component inputs are defined with properties like:
* **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):
* `name`, `display_name`
* Optional: `info`, `value`, `advanced`, `is_list`, `tool_mode`, `real_time_refresh`
async def_run(self):
# Custom execution logic here
# This runs instead of the default output method calls
pass
```
For example:
* **Store data in `self.ctx`**.
Use `self.ctx` in any of your component methods to share data between method calls.
```python
class MyComponent(Component):
* `StrInput`: simple text input.
* `DropdownInput`: selectable options.
* `HandleInput`: specialized connections.
def _pre_run_setup(self):
# Initialize counter in setup
self.ctx["processed_items"] = 0
Custom component `Output` properties define:
def process_data(self) -> Data:
# Increment counter during processing
self.ctx["processed_items"] += 1
return Data(data={"item": f"processed {self.ctx['processed_items']}"})
* `name`, `display_name`, `method`
* Optional: `info`
def get_summary(self) -> Data:
# Access counter in different method
total = self.ctx["processed_items"]
return Data(data={"summary": f"Processed {total} items total"})
```
For more information, see [Custom component inputs and outputs](/components-custom-components#custom-component-inputs-and-outputs).
## Inputs and outputs
### Associated Methods
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.
Each output is linked to a method:
### Inputs
* The output method name must match the method name.
* The method typically returns objects like Message, Data, or DataFrame.
* The method can use inputs with `self.<input_name>`.
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.
For example:
An input is usually an instance of a class from `langflow.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 langflow.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`.
**Additional parameters:**
* **`name`** - Internal variable name (accessed with `self.<name>`)
* **`display_name`** - Label shown in the visual editor
* **`value`** - Default value
* **`info`** - Tooltip or description
* **`required`** - Force user to provide a value
* **`advanced`** - Move field to "Advanced" section
* **`is_list`** - Allow multiple values
**Additional input types:**
* **Text**: `MultilineInput` (multi-line text area)
* **Numbers**: `IntInput`, `FloatInput`
* **Secrets**: `SecretStrInput` (hidden in UI)
* **Data**: `DataInput`, `MessageInput`, `MessageTextInput`
* **Files**: `FileInput`
* **Connections**: `HandleInput`
### 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 `langflow.io`.
For example, this component has one `output` that returns a `DataFrame`:
```python
from langflow.io import Output
from langflow.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 `DataFrame`. 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.
**Additional parameters:**
* **`name`** - Internal identifier for the output
* **`display_name`** - Label shown in the visual editor
* **`method`** - Name of the method called to produce the output
* **`info`** - Help text shown on hover
**Additional return types:**
* **`Message`** - Structured chat messages
* **`Data`** - Flexible object with `.data` and optional `.text`
* **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`, `Data`, or `DataFrame`, 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 `Data` object.
```python
Output(
display_name="File Contents",
name="file_contents",
display_name="File Contents",
method="read_file"
)
#...
def read_file(self) -> Data:
path = self.filename
with open(path, "r") as f:
@ -136,12 +322,13 @@ def read_file(self) -> Data:
return Data(data={"content": content})
```
### Components with multiple outputs
#### Components with multiple outputs
A component can define multiple outputs.
Each output can have a different corresponding method.
For example:
For example:
```python
outputs = [
Output(display_name="Processed Data", name="processed_data", method="process_data"),
@ -149,8 +336,6 @@ outputs = [
]
```
#### Output Grouping Behavior with `group_outputs`
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.
@ -168,7 +353,7 @@ This behavior is controlled by the `group_outputs` parameter:
<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:
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 = [
@ -188,9 +373,7 @@ outputs = [
</TabItem>
<TabItem value="true" label="True">
In this example, all outputs are available simultaneously in the visual editor:
2. `group_outputs=True`
In this example, all outputs are available simultaneously in the visual editor.
```python
outputs = [
@ -212,200 +395,7 @@ outputs = [
</TabItem>
</Tabs>
### Common internal patterns
#### `_pre_run_setup()`
To initialize a custom component with counters set:
```python
def _pre_run_setup(self):
if not hasattr(self, "_initialized"):
self._initialized = True
self.iteration = 0
```
#### Override `run` or `_run`
You can override `async def _run(self): ...` to define custom execution logic, although the default behavior from the base class usually covers most cases.
#### Store data in `self.ctx`
Use `self.ctx` as a shared storage for data or counters across the component's execution flow:
```python
def some_method(self):
count = self.ctx.get("my_count", 0)
self.ctx["my_count"] = count + 1
```
## Directory structure requirements
By default, Langflow looks for custom components in the `/components` directory.
If you're creating custom components in a different location using the `LANGFLOW_COMPONENTS_PATH` [environment variable](/environment-variables), components must be organized in a specific directory structure to be properly loaded and displayed in the visual editor:
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.
```
/your/custom/components/path/ # Base directory set by LANGFLOW_COMPONENTS_PATH
└── category_name/ # Required category subfolder that determines menu name
├── __init__.py # Required
└── custom_component.py # Component file
```
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 a component to the **Helpers** category, place it in the `helpers` subfolder:
```
/app/custom_components/ # LANGFLOW_COMPONENTS_PATH
└── helpers/ # Displayed within the "Helpers" category
├── __init__.py # Required
└── custom_component.py # Your component
```
You can have multiple category folders to organize components into different categories:
```
/app/custom_components/
├── helpers/
│ ├── __init__.py
│ └── helper_component.py
└── tools/
├── __init__.py
└── tool_component.py
```
This folder structure is required for Langflow to properly discover and load your custom components. Components placed directly in the base directory aren't loaded.
```
/app/custom_components/ # LANGFLOW_COMPONENTS_PATH
└── custom_component.py # Won't be loaded - missing category folder!
```
## Custom component inputs and outputs
Inputs and outputs 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 `langflow.io` (such as `StrInput`, `DataInput`, or `MessageTextInput`). The most common constructor parameters are:
* **`name`**: The internal variable name, accessed with `self.<name>`.
* **`display_name`**: The label shown to users in the visual editor.
* **`info`** *(optional)*: A tooltip or short description.
* **`value`** *(optional)*: The default value.
* **`advanced`** *(optional)*: If `true`, moves the field into the "Advanced" section.
* **`required`** *(optional)*: If `true`, forces the user to provide a value.
* **`is_list`** *(optional)*: If `true`, allows multiple values.
* **`input_types`** *(optional)*: Restricts allowed connection types (e.g., `["Data"]`, `["LanguageModel"]`).
Here are the most commonly used input classes and their typical usage.
**Text Inputs**: For simple text entries.
* **`StrInput`** creates a single-line text field.
* **`MultilineInput`** creates a multi-line text area.
**Numeric and Boolean Inputs**: Ensures users can only enter valid numeric or Boolean data.
* **`BoolInput`**, **`IntInput`**, and **`FloatInput`** provide fields for Boolean, integer, and float values, ensuring type consistency.
**Dropdowns**: For selecting from predefined options, useful for modes or levels.
* **`DropdownInput`**
**Secrets**: A specialized input for sensitive data, ensuring input is hidden in the visual editor.
* **`SecretStrInput`** for API keys and passwords.
**Specialized Data Inputs**: Ensures type-checking and color-coded connections in the visual editor.
* **`DataInput`** expects a `Data` object (typically with `.data` and optional `.text`).
* **`MessageInput`** expects a `Message` object, used in chat or agent flows.
* **`MessageTextInput`** simplifies access to the `.text` field of a `Message`.
**Handle-Based Inputs**: Used to connect outputs of specific types, ensuring correct pipeline connections.
- **`HandleInput`**
**File Uploads**: Allows users to upload files directly through the visual editor or receive file paths from other components.
- **`FileInput`**
**Lists**: Set `is_list=True` to accept multiple values, ideal for batch or grouped operations.
This example defines three inputs: a text field (`StrInput`), a Boolean toggle (`BoolInput`), and a dropdown selection (`DropdownInput`).
```python
from langflow.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")
]
```
### 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 `langflow.io`, with common parameters:
* **`name`**: The internal variable name.
* **`display_name`**: The label shown in the visual editor.
* **`method`**: The name of the method called to produce the output.
* **`info`** *(optional)*: Help text shown on hover.
The method must exist in the class, and it is recommended to annotate its return type for better type checking.
You can also set a `self.status` message inside the method to show progress or logs.
**Common Return Types**:
- **`Message`**: Structured chat messages.
- **`Data`**: Flexible object with `.data` and optional `.text`.
- **`DataFrame`**: Pandas-based tables (`langflow.schema.DataFrame`).
- **Primitive types**: `str`, `int`, `bool` (not recommended if you need type/color consistency).
In this example, the `DataToDataFrame` component defines its output using the outputs list. The `df_out` output is linked to the `build_df` method, so when connected to another component (node), Langflow calls this method and passes its returned `DataFrame` to the next node. This demonstrates how each output maps to a method that generates the actual output data.
```python
from langflow.custom import Component
from langflow.io import DataInput, Output
from langflow.schema import Data, DataFrame
class DataToDataFrame(Component):
display_name = "Data to DataFrame"
description = "Convert multiple Data objects into a DataFrame"
icon = "table"
name = "DataToDataFrame"
inputs = [
DataInput(
name="items",
display_name="Data Items",
info="List of Data objects to convert",
is_list=True
)
]
outputs = [
Output(
name="df_out",
display_name="DataFrame Output",
method="build_df"
)
]
def build_df(self) -> DataFrame:
rows = []
for item in self.items:
row_dict = item.data.copy() if item.data else {}
row_dict["text"] = item.get_text() or ""
rows.append(row_dict)
df = DataFrame(rows)
self.status = f"Built DataFrame with {len(rows)} rows."
return df
```
### Tool Mode
### 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.
@ -422,73 +412,65 @@ inputs = [
]
```
Langflow currently supports the following input types for **Tool Mode**:
* `DataInput`
* `DataFrameInput`
* `PromptInput`
* `MessageTextInput`
* `MultilineInput`
* `DropdownInput`
## Typed annotations
In Langflow, **typed annotations** allow Langflow to visually guide users and maintain flow consistency.
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 `Data`, `Message`, or `DataFrame` 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:
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.
* **Color-coding**: Outputs like `-> Data` or `-> Message` get distinct colors.
* **Validation**: Langflow blocks incompatible connections automatically.
* **Readability**: Developers can quickly understand data flow.
* **Development tools**: Better code suggestions and error checking in your code editor.
### Common return types
### Common Return Types
<Tabs>
<TabItem value="message" label="Message" default>
* `Message`: For chat-style outputs. Connects to any of several `Message`-compatible inputs.
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")
```
```python
def produce_message(self) -> Message:
return Message(text="Hello! from typed method!", sender="System")
```
* `Data`: For structured data like dicts or partial texts. Connects only to `DataInput` (ports that accept `Data`).
</TabItem>
<TabItem value="data" label="Data">
```python
def get_processed_data(self) -> Data:
processed = {"key1": "value1", "key2": 123}
return Data(data=processed)
```
For structured data like dicts or partial texts. Connects only to `DataInput` (ports that accept `Data`).
* `DataFrame`: For tabular data. Connects only to `DataFrameInput` (ports that accept `DataFrame`).
```python
def get_processed_data(self) -> Data:
processed = {"key1": "value1", "key2": 123}
return Data(data=processed)
```
```python
def build_df(self) -> DataFrame:
pdf = pd.DataFrame({"A": [1, 2], "B": [3, 4]})
return DataFrame(pdf)
```
</TabItem>
<TabItem value="dataframe" label="DataFrame">
* Primitive Types (`str`, `int`, `bool`): Returning primitives is allowed but wrapping in `Data` or `Message` is recommended for better consistency in the visual editor.
For tabular data. Connects only to `DataFrameInput` (ports that accept `DataFrame`).
```python
def compute_sum(self) -> int:
return sum(self.numbers)
```
```python
def build_df(self) -> DataFrame:
pdf = pd.DataFrame({"A": [1, 2], "B": [3, 4]})
return DataFrame(pdf)
```
### Tips for typed annotations
</TabItem>
<TabItem value="primitives" label="Primitive Types">
When using typed annotations, consider the following best practices:
Returning primitives is allowed, but wrapping in `Data` or `Message` is recommended for better consistency in the visual editor.
```python
def compute_sum(self) -> int:
return sum(self.numbers)
```
</TabItem>
</Tabs>
* **Always Annotate Outputs**: Specify return types like `-> Data`, `-> Message`, or `-> DataFrame` to enable proper visual editor color-coding and validation.
* **Wrap Raw Data**: Use `Data`, `Message`, or `DataFrame` wrappers instead of returning plain structures.
* **Use Primitives Carefully**: Direct `str` or `int` returns are fine for simple flows, but wrapping improves flexibility.
* **Annotate Helpers Too**: Even if internal, typing improves maintainability and clarity.
* **Handle Edge Cases**: Prefer returning structured `Data` with error fields when needed.
* **Stay Consistent**: Use the same types across your components to make flows predictable and easier to build.
## 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 **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`.
@ -518,11 +500,13 @@ class RegexRouter(Component):
]
```
### Implement `update_build_config`
### Show or hide fields based on user selections
When a field with `real_time_refresh=True` is modified, Langflow calls the `update_build_config` method, passing the updated field name, value, and the component's configuration to dynamically adjust the visibility or properties of other fields based on user input.
When a user changes a field with `real_time_refresh=True`, Langflow calls your `update_build_config` method.
This example will show or hide the `regex_pattern` field when the user selects a different operator.
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:
@ -534,89 +518,84 @@ def update_build_config(self, build_config: dict, field_value: str, field_name:
return build_config
```
### Additional Dynamic Field Controls
You can modify additional field properties in `update_build_config` other than just `show` and `hide`.
You can also modify other properties within `update_build_config`, such as:
* `required`: Set `build_config["some_field"]["required"] = True/False`
* **`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`: Set `build_config["some_field"]["advanced"] = True`
* `options`: Modify dynamic dropdown options.
### Tips for Managing Dynamic Fields
When working with dynamic fields, consider the following best practices to ensure a smooth user experience:
* **Minimize field changes**: Hide only fields that are truly irrelevant to avoid confusing users.
* **Test behavior**: Ensure that adding or removing fields doesn't accidentally erase user input.
* **Preserve data**: Use `build_config["some_field"]["show"] = False` to hide fields without losing their values.
* **Clarify logic**: Add `info` notes to explain why fields appear or disappear based on conditions.
* **Keep it manageable**: If the dynamic logic becomes too complex, consider breaking it into smaller components, unless it serves a clear purpose in a single node.
* **`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
In Langflow, robust error handling ensures that your components behave predictably, even when unexpected situations occur, such as invalid inputs, external API failures, or internal logic errors.
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.
### Error handling techniques
```python
def compute_result(self) -> str:
if not self.user_input:
raise ValueError("No input provided.")
# ...
```
* **Raise Exceptions**: If a critical error occurs, you can raise standard Python exceptions such as `ValueError`, or specialized exceptions like `ToolException`. Langflow will automatically catch these and display appropriate error messages in the visual editor, helping users quickly identify what went wrong.
Alternatively, instead of stopping a flow abruptly, you can return a `Data` 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 compute_result(self) -> str:
if not self.user_input:
raise ValueError("No input provided.")
```python
def run_model(self) -> Data:
try:
# ...
```
except Exception as e:
return Data(data={"error": str(e)})
```
* **Return Structured Error Data**: Instead of stopping a flow abruptly, you can return a Data object containing an "error" field. This approach allows the flow to continue operating and enables downstream components to detect and handle the error gracefully.
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 run_model(self) -> Data:
try:
# ...
except Exception as e:
return Data(data={"error": str(e)})
```
```python
def parse_data(self) -> Data:
# ...
self.status = f"Parsed {len(rows)} rows successfully."
return Data(data={"rows": rows})
```
### Improve debugging and flow management
You can halt individual output paths when certain conditions fail using `self.stop()`, without stopping other outputs from the same component.
* **Use `self.status`**: Each component has a status field where you can store short messages about the execution result—such as success summaries, partial progress, or error notifications. These appear directly in the visual editor, making troubleshooting easier for users.
This example stops the output if the user input is empty, preventing the component from processing invalid data.
```python
def parse_data(self) -> Data:
# ...
self.status = f"Parsed {len(rows)} rows successfully."
return Data(data={"rows": rows})
```
```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"})
```
* **Stop specific outputs with `self.stop(...)`**: You can halt individual output paths when certain conditions fail, without affecting the entire component. This is especially useful when working with components that have multiple output branches.
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.
```python
def some_output(self) -> Data:
if <some condition>:
self.stop("some_output") # Tells Langflow no data flows
return Data(data={"error": "Condition not met"})
```
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).
* **Log events**: You can log key execution details inside components. Logs are displayed in the "Logs" or "Events" section of the component's detail view and can be accessed later through the flow's debug panel or exported files, providing a clear trace of the component's behavior for easier debugging.
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}")
# ...
```
### Tips for error handling and logging
To build more reliable components, consider the following best practices:
* **Validate inputs early**: Catch missing or invalid inputs at the start to prevent broken logic.
* **Summarize with `self.status`**: Use short success or error summaries to help users understand results quickly.
* **Keep logs concise**: Focus on meaningful messages to avoid cluttering the visual editor.
* **Return structured errors**: When appropriate, return `Data(data={"error": ...})` instead of raising exceptions to allow downstream handling.
* **Stop outputs selectively**: Only halt specific outputs with `self.stop(...)` if necessary, to preserve correct flow behavior elsewhere.
```python
def process_file(self, file_path: str):
self.log(f"Processing file {file_path}")
```
## Contribute custom components to Langflow
See [How to Contribute](/contributing-components) to contribute your custom component to Langflow.
To contribute your custom component to the Langflow project, see [Contribute components](/contributing-components).

View File

@ -1,5 +1,5 @@
---
title: Contribute bundles
title: Contribute component bundles
slug: /contributing-bundles
---
@ -11,24 +11,26 @@ If you want to contribute your custom components back to the Langflow project, y
Follow these steps to add components to <Icon name="Blocks" aria-hidden="true" /> **Bundles** in the Langflow visual editor.
This example adds a bundle named `DarthVader`.
## Add the bundle to the backend folder
For more information on creating custom components, see [Create custom Python components](/components-custom-components).
1. Navigate to the backend directory in the Langflow repository and create a new folder for your bundle.
The path for your new component is `src > lfx > src > lfx > components > darth_vader`.
## Add the bundle to the lfx components folder
1. Navigate to the lfx directory in the Langflow repository and create a new folder for your bundle.
The path for your new component is `src/lfx/src/lfx/components/darth_vader`.
You can view the [components folder](https://github.com/langflow-ai/langflow/tree/main/src/lfx/src/lfx/components) in the Langflow repository.
2. Within the newly created `darth_vader` folder, add the following files:
* `darth_vader_component.py` — This file contains the backend logic for the new bundle. Create multiple `.py` files for multiple components.
* `__init__.py` — This file initializes the bundle components. You can use any existing `__init__.py` as an example to see how it should be structured.
* `darth_vader_component.py` — This file contains the backend logic for the new bundle. Create multiple `.py` files for multiple components.
* `__init__.py` — This file initializes the bundle components. You can use any existing `__init__.py` as an example to see how it should be structured.
For an example of adding multiple components in a bundle, see the [Notion](https://github.com/langflow-ai/langflow/tree/main/src/lfx/src/lfx/components/Notion) bundle.
For an example of adding multiple components in a bundle, see the [Notion](https://github.com/langflow-ai/langflow/tree/main/src/lfx/src/lfx/components/Notion) bundle.
## Add the bundle to the frontend folder
1. Navigate to the frontend directory in the Langflow repository to add your bundle's icon.
The path for your new component icon is `src > frontend > src > icons > DarthVader`
The path for your new component icon is `src/frontend/src/icons/DarthVader`
You can view the [icons folder](https://github.com/langflow-ai/langflow/tree/main/src/frontend/src/icons) in the Langflow repository.
To add your icon, create **three** files inside the `icons/darth_vader` folder.
@ -126,7 +128,7 @@ For example:
In your component bundle, associate the icon variable with your new bundle.
In your `darth_vader_component.py` file, in the component class, include the icon that you defined in the frontend.
The `icon` must point to the directory you created for your icons within the `src > frontend > src > icons` directory.
The `icon` must point to the directory you created for your icons within the `src/frontend/src/icons` directory.
For example:
```
class DarthVaderAPIComponent(LCToolComponent):

View File

@ -9,17 +9,17 @@ This guide outlines how to structure and implement tests for application compone
* The test file should follow the same directory structure as the component being tested, but should be placed in the corresponding unit tests folder.
For example, if the file path for the component is `src/backend/base/langflow/components/prompts/`, then the test file should be located at `src/backend/tests/unit/components/prompts`.
For example, if the file path for the component is `src/lfx/src/lfx/components/data/`, then the test file should be located at `src/backend/tests/unit/components/data`.
* The test file name should use snake case and follow the pattern `test_<file_name>.py`.
For example, if the file to be tested is `PromptComponent.py`, then the test file should be named `test_prompt_component.py`.
For example, if the file to be tested is `FileComponent.py`, then the test file should be named `test_file_component.py`.
## File structure
* Each test file should group tests into classes by component. There should be no standalone test functions in the file— only test methods within classes.
* Class names should follow the pattern `Test<ClassName>`.
For example, if the component being tested is `PromptComponent`, then the test class should be named `TestPromptComponent`.
For example, if the component being tested is `FileComponent`, then the test class should be named `TestFileComponent`.
## Imports, inheritance, and mandatory methods
@ -39,7 +39,7 @@ These base classes enforce mandatory methods that the component test classes mus
```python
@pytest.fixture
def component_class(self):
return PromptComponent
return FileComponent
```
* `default_kwargs:` Returns a dictionary with the default arguments required to instantiate the component. For example:
@ -47,7 +47,7 @@ These base classes enforce mandatory methods that the component test classes mus
```python
@pytest.fixture
def default_kwargs(self):
return {"template": "Hello {name}!", "name": "John", "_session_id": "123"}
return {"file_path": "/tmp/test.txt", "_session_id": "123"}
```
* `file_names_mapping:` Returns a list of dictionaries representing the relationship between `version`, `module`, and `file_name` that the tested component has had over time. This can be left empty if it is an unreleased component. For example:
@ -56,11 +56,11 @@ These base classes enforce mandatory methods that the component test classes mus
@pytest.fixture
def file_names_mapping(self):
return [
{"version": "1.0.15", "module": "prompts", "file_name": "Prompt"},
{"version": "1.0.16", "module": "prompts", "file_name": "Prompt"},
{"version": "1.0.17", "module": "prompts", "file_name": "Prompt"},
{"version": "1.0.18", "module": "prompts", "file_name": "Prompt"},
{"version": "1.0.19", "module": "prompts", "file_name": "Prompt"},
{"version": "1.0.15", "module": "data", "file_name": "File"},
{"version": "1.0.16", "module": "data", "file_name": "File"},
{"version": "1.0.17", "module": "data", "file_name": "File"},
{"version": "1.0.18", "module": "data", "file_name": "File"},
{"version": "1.0.19", "module": "data", "file_name": "File"},
]
```
@ -101,14 +101,13 @@ Once the basic structure of the test file is defined, implement test methods for
After executing the `.to_frontend_node()` method, the resulting data is available for verification in the dictionary `frontend_node["data"]["node"]`. Assertions should be clear and cover the expected outcomes.
```python
def test_post_code_processing(self, component_class, default_kwargs):
def test_file_component_processing(self, component_class, default_kwargs):
component = component_class(**default_kwargs)
frontend_node = component.to_frontend_node()
node_data = frontend_node["data"]["node"]
assert node_data["template"]["template"]["value"] == "Hello {name}!"
assert "name" in node_data["custom_fields"]["template"]
assert "name" in node_data["template"]
assert node_data["template"]["name"]["value"] == "John"
assert node_data["template"]["path"]["file_path"] == "/tmp/test.txt"
assert "path" in node_data["template"]
assert node_data["display_name"] == "File"
```

View File

@ -3,6 +3,7 @@ title: Contribute components
slug: /contributing-components
---
import PartialBasicComponentStructure from '../_partial-basic-component-structure.mdx';
New components are added as objects of the [`Component`](https://github.com/langflow-ai/langflow/blob/main/src/lfx/src/lfx/custom/custom_component/component.py) class.
@ -12,75 +13,13 @@ Dependencies are added to the [pyproject.toml](https://github.com/langflow-ai/la
Anyone can contribute an example component. For example, to create a new data component called **DataFrame processor**, follow these steps to contribute it to Langflow.
1. Create a Python file called `dataframe_processor.py`.
2. Write your processor as an object of the [`Component`](https://github.com/langflow-ai/langflow/blob/main/src/lfx/src/lfx/custom/custom_component/component.py) class. You'll create a new class, `DataFrameProcessor`, that will inherit from `Component` and override the base class's methods.
```python
from typing import Any, Dict, Optional
import pandas as pd
from langflow.custom import Component
class DataFrameProcessor(Component):
"""A component that processes pandas DataFrames with various operations."""
```
3. Define class attributes to provide information about your custom component:
```python
from typing import Any, Dict, Optional
import pandas as pd
from langflow.custom import Component
class DataFrameProcessor(Component):
"""A component that processes pandas DataFrames with various operations."""
display_name: str = "DataFrame Processor"
description: str = "Process and transform pandas DataFrames with various operations like filtering, sorting, and aggregation."
documentation: str = "https://docs.langflow.org/components-dataframe-processor"
icon: str = "DataframeIcon"
priority: int = 100
name: str = "dataframe_processor"
```
* `display_name`: A user-friendly name shown in the visual editor.
* `description`: A brief description of what your component does.
* `documentation`: A link to detailed documentation.
* `icon`: An emoji or icon identifier for visual representation.
For more information, see [Contributing bundles](/contributing-bundles#add-the-bundle-to-the-frontend-folder).
* `priority`: An optional integer to control display order. Lower numbers appear first.
* `name`: An optional internal identifier that defaults to class name.
4. Define the component's interface by specifying its inputs, outputs, and the method that will process them. The method name must match the `method` field in your outputs list, as this is how Langflow knows which method to call to generate each output.
This example creates a minimal custom component skeleton.
For more information on creating your custom component, see [Create custom Python components](/components-custom-components).
```python
from typing import Any, Dict, Optional
import pandas as pd
from langflow.custom import Component
class DataFrameProcessor(Component):
"""A component that processes pandas DataFrames with various operations."""
display_name: str = "DataFrame Processor"
description: str = "Process and transform pandas DataFrames with various operations like filtering, sorting, and aggregation."
documentation: str = "https://docs.langflow.org/components-dataframe-processor"
icon: str = "DataframeIcon"
priority: int = 100
name: str = "dataframe_processor"
# input and output lists
inputs = []
outputs = []
# method
def some_output_method(self):
return ...
```
<PartialBasicComponentStructure />
5. Save the `dataframe_processor.py` to the `src/lfx/src/lfx/components` directory.
This example adds a data component, so add it to the `/data` directory.
6. Add the component dependency to `src > lfx > src > lfx > components > data > __init__.py` as `from .DataFrameProcessor import DataFrameProcessor`.
You can view the [/data/__init__.py](https://github.com/langflow-ai/langflow/blob/main/src/lfx/src/lfx/components/data/__init__.py) in the Langflow repository.
6. Add the component dependency to `src/lfx/src/lfx/components/data/__init__.py` as `from .DataFrameProcessor import DataFrameProcessor`.
You can view the [/data/__init__.py](https://github.com/langflow-ai/langflow/blob/dev/src/lfx/src/lfx/components/data/__init__.py) in the Langflow repository.
7. Add any new dependencies to the [pyproject.toml](https://github.com/langflow-ai/langflow/blob/main/pyproject.toml#L20) file.

View File

@ -159,7 +159,7 @@ FROM langflowai/langflow:latest
WORKDIR /app
# Copy your modified memory component
COPY src/backend/base/langflow/components/helpers/memory.py /tmp/memory.py
COPY src/lfx/src/lfx/components/helpers/memory.py /tmp/memory.py
# Find the site-packages directory where langflow is installed
RUN python -c "import site; print(site.getsitepackages()[0])" > /tmp/site_packages.txt
@ -194,7 +194,7 @@ To use this custom Dockerfile, do the following:
In this example, Langflow expects `memory.py` to exist in the `/helpers` directory, so you create a directory in that location.
```bash
mkdir -p src/backend/base/langflow/components/helpers
mkdir -p src/lfx/src/lfx/components/helpers
```
3. Place your modified `memory.py` file in the `/helpers` directory.

View File

@ -0,0 +1,70 @@
1. Create a Python file for your component, such as `dataframe_processor.py`.
2. Write your component as an object of the [`Component`](https://github.com/langflow-ai/langflow/blob/main/src/backend/base/langflow/custom/custom_component/component.py) class. Create a new class that inherits from `Component` and override the base class's methods.
:::tip Backwards compatibility
The `lfx` import path replaced the `import from langflow.custom import Component` in Langflow 1.7, but the original input is still compatible and works the same way.
:::
```python
from typing import Any, Dict, Optional
import pandas as pd
from lfx.custom.custom_component.component import Component
class DataFrameProcessor(Component):
"""A component that processes pandas DataFrames with various operations."""
```
3. Define class attributes to provide information about your custom component:
```python
from typing import Any, Dict, Optional
import pandas as pd
from lfx.custom.custom_component.component import Component
class DataFrameProcessor(Component):
"""A component that processes pandas DataFrames with various operations."""
display_name: str = "DataFrame Processor"
description: str = "Process and transform pandas DataFrames with various operations like filtering, sorting, and aggregation."
documentation: str = "https://docs.langflow.org/components-dataframe-processor"
icon: str = "DataframeIcon"
priority: int = 100
name: str = "dataframe_processor"
```
* `display_name`: A user-friendly name shown in the visual editor.
* `description`: A brief description of what your component does.
* `documentation`: A link to detailed documentation.
* `icon`: An emoji or icon identifier for visual representation.
Langflow uses [Lucide](https://lucide.dev/icons) for icons. To assign an icon to your component, set the icon attribute to the name of a Lucide icon as a string, such as `icon = "file-text"`. Langflow renders icons from the Lucide library automatically.
For more information, see [Contributing bundles](/contributing-bundles#add-the-bundle-to-the-frontend-folder).
* `priority`: An optional integer to control display order. Lower numbers appear first.
* `name`: An optional internal identifier that defaults to class name.
4. Define the component's interface by specifying its inputs, outputs, and the method that will process them. The method name must match the `method` field in your outputs list, as this is how Langflow knows which method to call to generate each output.
This example creates a minimal custom component skeleton.
```python
from typing import Any, Dict, Optional
import pandas as pd
from lfx.custom.custom_component.component import Component
class DataFrameProcessor(Component):
"""A component that processes pandas DataFrames with various operations."""
display_name: str = "DataFrame Processor"
description: str = "Process and transform pandas DataFrames with various operations like filtering, sorting, and aggregation."
documentation: str = "https://docs.langflow.org/components-dataframe-processor"
icon: str = "DataframeIcon"
priority: int = 100
name: str = "dataframe_processor"
# input and output lists
inputs = []
outputs = []
# method
def some_output_method(self):
return ...
```

View File

@ -425,9 +425,9 @@ module.exports = {
"Contributing/contributing-community",
"Contributing/contributing-how-to-contribute",
"Contributing/contributing-components",
"Contributing/contributing-bundles",
"Contributing/contributing-component-tests",
"Contributing/contributing-templates",
"Contributing/contributing-bundles",
],
},
{