Create custom Python components
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.
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.
Class-level attributes
Define these attributes to control a custom component's appearance and behavior:
_10class MyCsvReader(Component):_10 display_name = "CSV Reader"_10 description = "Reads CSV files"_10 icon = "file-text"_10 name = "CSVReader"_10 documentation = "http://docs.example.com/csv_reader"
-
display_name
: A user-friendly label shown in the Components menu and on the component itself when you add it to a flow. -
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.Langflow uses Lucide 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. -
name
: A unique internal identifier. -
documentation
: An optional link to external documentation, such as API or product documentation.
Structure of a custom component
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.
A basic component:
- Inherits from
langflow.custom.Component
. - Declares metadata like
display_name
,description
,icon
, and more. - Defines
inputs
andoutputs
lists. - Implements methods matching output specifications.
A minimal custom component skeleton contains the following:
_14from langflow.custom import Component_14from langflow.template import Output_14_14class MyComponent(Component):_14 display_name = "My Component"_14 description = "A short summary."_14 icon = "sparkles"_14 name = "MyComponent"_14_14 inputs = []_14 outputs = []_14_14 def some_output_method(self):_14 return ...
Internal Lifecycle and Execution Flow
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()
orbuild_results()
triggers output methods.
Optional Hooks:
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.
Inputs and outputs
Custom component inputs are defined with properties like:
name
,display_name
- Optional:
info
,value
,advanced
,is_list
,tool_mode
,real_time_refresh
For example:
StrInput
: simple text input.DropdownInput
: selectable options.HandleInput
: specialized connections.
Custom component Output
properties define:
name
,display_name
,method
- Optional:
info
For more information, see Custom component inputs and outputs.
Associated Methods
Each output is linked to a method:
- 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>
.
For example:
_12Output(_12 display_name="File Contents",_12 name="file_contents",_12 method="read_file"_12)_12#..._12def read_file(self) -> Data:_12 path = self.filename_12 with open(path, "r") as f:_12 content = f.read()_12 self.status = f"Read {len(content)} chars from {path}"_12 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:
_10outputs = [_10 Output(display_name="Processed Data", name="processed_data", method="process_data"),_10 Output(display_name="Debug Info", name="debug_info", method="provide_debug_info"),_10]
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.
This behavior is controlled by the group_outputs
parameter:
-
group_outputs=False
(default): When a component has more than one output andgroup_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.
- False or not set
- True
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:
_12outputs = [_12 Output(_12 name="structured_output",_12 display_name="Structured Output",_12 method="build_structured_output",_12 ),_12 Output(_12 name="dataframe_output",_12 display_name="DataFrame Output",_12 method="build_structured_dataframe",_12 ),_12]
In this example, all outputs are available simultaneously in the visual editor:
group_outputs=True
_14outputs = [_14 Output(_14 name="true_result",_14 display_name="True",_14 method="true_response",_14 group_outputs=True,_14 ),_14 Output(_14 name="false_result",_14 display_name="False",_14 method="false_response",_14 group_outputs=True,_14 ),_14]
Common internal patterns
_pre_run_setup()
To initialize a custom component with counters set:
_10def _pre_run_setup(self):_10 if not hasattr(self, "_initialized"):_10 self._initialized = True_10 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:
_10def some_method(self):_10 count = self.ctx.get("my_count", 0)_10 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, components must be organized in a specific directory structure to be properly loaded and displayed in the visual editor:
_10/your/custom/components/path/ # Base directory set by LANGFLOW_COMPONENTS_PATH_10 └── category_name/ # Required category subfolder that determines menu name_10 └── 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 Components menu.
For example, to add a component to the Helpers category, place it in the helpers
subfolder:
_10/app/custom_components/ # LANGFLOW_COMPONENTS_PATH_10 └── helpers/ # Displayed within the "Helpers" category_10 └── custom_component.py # Your component
You can have multiple category folders to organize components into different categories:
_10/app/custom_components/_10 ├── helpers/_10 │ └── helper_component.py_10 └── tools/_10 └── 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.
_10/app/custom_components/ # LANGFLOW_COMPONENTS_PATH_10 └── 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 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 withself.<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
, andFloatInput
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 aData
object (typically with.data
and optional.text
).MessageInput
expects aMessage
object, used in chat or agent flows.MessageTextInput
simplifies access to the.text
field of aMessage
.
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
).
_10from langflow.io import StrInput, BoolInput, DropdownInput_10_10inputs = [_10 StrInput(name="title", display_name="Title"),_10 BoolInput(name="enabled", display_name="Enabled", value=True),_10 DropdownInput(name="mode", display_name="Mode", options=["Fast", "Safe", "Experimental"], value="Safe")_10]
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.
_37from langflow.custom import Component_37from langflow.io import DataInput, Output_37from langflow.schema import Data, DataFrame_37_37class DataToDataFrame(Component):_37 display_name = "Data to DataFrame"_37 description = "Convert multiple Data objects into a DataFrame"_37 icon = "table"_37 name = "DataToDataFrame"_37_37 inputs = [_37 DataInput(_37 name="items",_37 display_name="Data Items",_37 info="List of Data objects to convert",_37 is_list=True_37 )_37 ]_37_37 outputs = [_37 Output(_37 name="df_out",_37 display_name="DataFrame Output",_37 method="build_df"_37 )_37 ]_37_37 def build_df(self) -> DataFrame:_37 rows = []_37 for item in self.items:_37 row_dict = item.data.copy() if item.data else {}_37 row_dict["text"] = item.get_text() or ""_37 rows.append(row_dict)_37_37 df = DataFrame(rows)_37 self.status = f"Built DataFrame with {len(rows)} rows."_37 return df
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
:
_10inputs = [_10 MessageTextInput(_10 name="message",_10 display_name="Mensage",_10 info="Enter the message that will be processed directly by the tool",_10 tool_mode=True,_10 ),_10]
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.
Typed annotations provide:
- 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
-
Message
: For chat-style outputs. Connects to any of severalMessage
-compatible inputs._10def produce_message(self) -> Message:_10return Message(text="Hello! from typed method!", sender="System") -
Data
: For structured data like dicts or partial texts. Connects only toDataInput
(ports that acceptData
)._10def get_processed_data(self) -> Data:_10processed = {"key1": "value1", "key2": 123}_10return Data(data=processed) -
DataFrame
: For tabular data. Connects only toDataFrameInput
(ports that acceptDataFrame
)._10def build_df(self) -> DataFrame:_10pdf = pd.DataFrame({"A": [1, 2], "B": [3, 4]})_10return DataFrame(pdf) -
Primitive Types (
str
,int
,bool
): Returning primitives is allowed but wrapping inData
orMessage
is recommended for better consistency in the visual editor._10def compute_sum(self) -> int:_10return sum(self.numbers)
Tips for typed annotations
When using typed annotations, consider the following best practices:
- 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
, orDataFrame
wrappers instead of returning plain structures. - Use Primitives Carefully: Direct
str
orint
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 this example, the operator field triggers updates with real_time_refresh=True
.
The regex_pattern
field is initially hidden and controlled with dynamic=True
.
_22from langflow.io import DropdownInput, StrInput_22_22class RegexRouter(Component):_22 display_name = "Regex Router"_22 description = "Demonstrates dynamic fields for regex input."_22_22 inputs = [_22 DropdownInput(_22 name="operator",_22 display_name="Operator",_22 options=["equals", "contains", "regex"],_22 value="equals",_22 real_time_refresh=True,_22 ),_22 StrInput(_22 name="regex_pattern",_22 display_name="Regex Pattern",_22 info="Used if operator='regex'",_22 dynamic=True,_22 show=False,_22 ),_22 ]
Implement update_build_config
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.
This example will show or hide the regex_pattern
field when the user selects a different operator.
_10def update_build_config(self, build_config: dict, field_value: str, field_name: str | None = None) -> dict:_10 if field_name == "operator":_10 if field_value == "regex":_10 build_config["regex_pattern"]["show"] = True_10 else:_10 build_config["regex_pattern"]["show"] = False_10 return build_config
Additional Dynamic Field Controls
You can also modify other properties within update_build_config
, such as:
-
required
: Setbuild_config["some_field"]["required"] = True/False
-
advanced
: Setbuild_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.
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.
Error handling techniques
-
Raise Exceptions: If a critical error occurs, you can raise standard Python exceptions such as
ValueError
, or specialized exceptions likeToolException
. Langflow will automatically catch these and display appropriate error messages in the visual editor, helping users quickly identify what went wrong._10def compute_result(self) -> str:_10if not self.user_input:_10raise ValueError("No input provided.")_10# ... -
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.
_10def run_model(self) -> Data:_10try:_10# ..._10except Exception as e:_10return Data(data={"error": str(e)})
Improve debugging and flow management
-
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._10def parse_data(self) -> Data:_10# ..._10self.status = f"Parsed {len(rows)} rows successfully."_10return Data(data={"rows": rows}) -
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._10def some_output(self) -> Data:_10if <some condition>:_10self.stop("some_output") # Tells Langflow no data flows_10return Data(data={"error": "Condition not met"}) -
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.
_10def process_file(self, file_path: str):_10self.log(f"Processing file {file_path}")_10# ...
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.
Contribute custom components to Langflow
See How to Contribute to contribute your custom component to Langflow.