Create a ReAct agent with LangGraph in 80 lines of code

5 min read

TL;DR

  • create_react_agent from langgraph.prebuilt is the fastest path to a running tool-calling agent, but the function carries a deprecation warning in LangGraph 0.3.x in favor of create_agent.
  • langgraph create react agent generates a 2-node StateGraph (agent + ToolNode) with an automatic tools_condition edge that drives the reasoning loop.
  • Three extension points work without leaving the prebuilt: checkpointers, system prompt via state_modifier, and interrupt_before=["tools"] for human approval gating.
  • When parallel fan-out, multi-agent subgraphs, or custom state keys are needed, drop to a raw StateGraph and use the langchain-ai/react-agent template as a scaffold.

The fastest on-ramp to a reasoning loop in LangGraph is create_react_agent. One function call gives you a langgraph create react agent that runs the full ReAct cycle without writing the underlying StateGraph by hand. The catch: the function is deprecated in LangGraph 0.3.x, and first-timers hit the warning before they understand what it builds. This article walks through a complete 80-line implementation, opens the black box to show the generated graph, and maps the four signals that mean you should drop the prebuilt and write the graph yourself.

What create_react_agent does, and the deprecation you need to know about

The ReAct pattern originates from Yao et al. 2022 (arXiv:2210.03629): alternate between a reasoning step (the LLM decides what to do) and an acting step (call a tool, observe the result), loop until the model returns a final answer without a tool call. That pattern maps onto a graph directly: one node for the LLM call, one node for tool execution, one conditional edge that routes between them.

create_react_agent shipped in LangGraph 0.1 (2024) to package that graph into a single callable. It generates a MessagesState graph, wires a ToolNode, and sets the tools_condition edge automatically. The deprecation notice visible in LangGraph 0.3.x reads: "this function is deprecated in favor of create_agent." The migration path is straightforward because the signature is nearly identical. Once the successor API stabilizes in 2026, swapping is a one-line import change.

For now, create_react_agent still ships in langgraph.prebuilt and produces the same graph topology. The warning is worth knowing so you are not caught off guard when running the implementation below.

How to use langgraph create react agent: the complete 80-line implementation

Installation and model adapter

pip install "langgraph>=0.2" langchain-openai langchain-core
export OPENAI_API_KEY="sk-..."

gpt-4o-mini is the default choice for development iteration: $0.15 per 1M input tokens as of Q1 2026, making it practical to iterate through the autonomous LLM agent's multi-turn cycles without meaningful cost. Swap in claude-3-5-haiku-20241022 via langchain-anthropic if you prefer Anthropic's model tier at a comparable price point.

Defining tools and running the agent

## pip install "langgraph>=0.2" langchain-openai langchain-core

from langgraph.prebuilt import create_react_agent
from langgraph.checkpoint.memory import MemorySaver
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
from langchain_core.messages import HumanMessage

## ----------------------------------------------------------------
## 1. Tool definitions
## ----------------------------------------------------------------

@tool
def get_weather(city: str) -> str:
    """Return current weather for a city (stub — replace with a live API)."""
    return f"The weather in {city} is 22°C and sunny."

@tool
def calculate(expression: str) -> str:
    """Evaluate a safe arithmetic expression and return the result."""
    allowed = set("0123456789+-*/(). ")
    if not set(expression).issubset(allowed):
        return "Error: unsupported characters."
    try:
        return str(eval(expression))  # noqa: S307
    except Exception as exc:
        return f"Error: {exc}"

@tool
def search_docs(query: str) -> str:
    """Search internal documentation (stub — plug in a real retriever)."""
    return f"No results for: {query}"

## ----------------------------------------------------------------
## 2. Model + ReAct graph
## ----------------------------------------------------------------

model = ChatOpenAI(model="gpt-4o-mini", temperature=0)
tools = [get_weather, calculate, search_docs]

SYSTEM_PROMPT = (
    "You are a precise assistant. "
    "Use a tool whenever external information or computation is needed. "
    "Cite the tool name in your final answer."
)

checkpointer = MemorySaver()  # RAM only; swap SqliteSaver for persistence

agent = create_react_agent(
    model,
    tools,
    state_modifier=SYSTEM_PROMPT,
    checkpointer=checkpointer,
)

## ----------------------------------------------------------------
## 3. First turn — stream intermediate steps
## ----------------------------------------------------------------

thread_cfg = {"configurable": {"thread_id": "demo-01"}}
query = "What is the weather in Paris? Also compute 1337 * 42."
print(f"User: {query}\n")

for chunk in agent.stream(
    {"messages": [HumanMessage(content=query)]},
    config=thread_cfg,
    stream_mode="values",
):
    msg = chunk["messages"][-1]
    if msg.content:
        print(f"[{msg.__class__.__name__}] {msg.content}")

## ----------------------------------------------------------------
## 4. Follow-up turn — checkpointer restores thread automatically
## ----------------------------------------------------------------

followup = "Which tools did you call in the previous answer?"
print(f"\nUser: {followup}\n")
result = agent.invoke(
    {"messages": [HumanMessage(content=followup)]},
    config=thread_cfg,
)
print(result["messages"][-1].content)

Run this file and you see the agent emit a ToolMessage for each call before producing a final AIMessage. The stream_mode="values" parameter surfaces every intermediate state, letting you inspect the tool-calling agent's reasoning loop without additional instrumentation.

Under the hood: the StateGraph that create_react_agent builds for you

The prebuilt generates a graph with exactly two named nodes: agent (the LLM call) and tools (the ToolNode). Three edges connect them: START to agent, a conditional tools_condition edge from agent that routes to either tools or END, and an unconditional edge from tools back to agent.

MessagesState is the base state class: a single messages key that appends (never overwrites) each new BaseMessage. This append-only behavior handles multi-turn memory without any explicit bookkeeping. Every tool call, observation, and LLM response accumulates in the same list, and the model receives the full history on each invocation.

The readable skeleton for this AI agent graph lives in the langchain-ai/react-agent GitHub template. Cloning that repository shows you the raw StateGraph construction that create_react_agent packages, and it is the natural starting point when the prebuilt becomes too constrained for your use case.

Extending the prebuilt: checkpointers, system prompts, and human-in-the-loop interrupts

Three extension points let you push the agent orchestration framework further without abandoning create_react_agent.

Checkpointers. MemorySaver stores state in process memory, lost on process exit. SqliteSaver from langgraph.checkpoint.sqlite serializes each checkpoint to a local .db file, making threads durable across restarts. According to benchmarks shared in the LangGraph Discord in November 2025, SqliteSaver adds approximately 2 ms of overhead per checkpoint on a local SSD. That overhead is negligible for interactive agents and only worth considering in high-throughput batch workloads.

System prompt injection. The state_modifier parameter accepts a string or a callable. A string becomes a SystemMessage prepended to the message list on every turn. A callable receives the full MessagesState and returns a modified state, enabling dynamic prompting logic that changes per turn based on conversation context.

Human-in-the-loop gating. Pass interrupt_before=["tools"] to create_react_agent and the graph pauses before every tool execution, surfacing the pending ToolCall objects to your application layer. Your code can inspect, modify, or reject the proposed call before resuming with a subsequent agent.invoke. This is the standard way to add an approval workflow to a reasoning loop without writing a custom graph.

When to drop create_react_agent and build the graph yourself

The prebuilt covers the common case well. Four concrete signals indicate it has become too rigid.

Parallel tool fan-out. When you want the agent to dispatch multiple tool calls simultaneously and join results before reasoning again, you need a custom graph with the Send API for concurrent node execution. I've seen teams try to hack this with async wrappers around the prebuilt (it doesn't work well).

Multi-agent supervisor patterns. Routing between specialist subgraphs from an orchestrator graph is not expressible in the prebuilt's two-node topology. That pattern requires a top-level graph with explicit subgraph invocations. Think of it like microservices, but for AI agents.

Non-messages state keys. MessagesState only streams messages. If your tools produce structured intermediate artifacts (retrieved chunks, image embeddings, structured JSON) that you want to expose as named state fields, you need a custom TypedDict state class. The prebuilt assumes everything flows through the message list, which gets messy fast with complex data.

Step-level routing logic. If tool results should change the next-step decision based on state fields beyond messages, the tools_condition edge is too coarse. A raw StateGraph lets you define arbitrary routing functions that can branch on any state property.

The 2026 LangGraph ecosystem offers three concrete resources for those situations. The langchain-ai/langgraph repository (which crossed 40k GitHub stars by Q1 2026) is the primary reference for raw StateGraph construction. LangGraph Studio, the visual graph debugger, has been available for local use since Q4 2024 and lets you step through node executions with a trace UI. LangGraph Platform reached general availability in late 2025 with a free tier covering 1M node executions per month, removing the need to operate a runtime server.

The official successor to langgraph create react agent is create_agent, currently in active development in the prebuilt module, with a near-identical signature that makes migration a one-line change once the API stabilizes.

Key takeaways

create_react_agent is the correct starting point for any tool-calling agent in LangGraph: it delivers a complete reasoning loop with zero graph boilerplate, and supports checkpointers, system prompt injection, and human-in-the-loop gating without leaving the prebuilt. When you need parallel fan-out, multi-agent routing, or custom intermediate state keys, the raw StateGraph and the langchain-ai/react-agent template are the two natural next steps. Track create_agent as the migration target for 2026.


Most teams hit the deprecation warning before they understand what create_react_agent actually builds. Drop into the black box with the production CLAUDE.md template in the kit to see what a real ReAct agent config looks like when it ships.

Get the welcome kit