| name | langchain-text-splitters |
| description | Guide to using text splitter integrations in LangChain including recursive, character, and semantic splitters |
| language | js |
langchain-text-splitters (JavaScript/TypeScript)
Overview
Text splitters divide large documents into smaller chunks that fit within model context windows and enable effective retrieval. Proper chunking is critical for RAG system performance - chunks must be small enough for retrieval but large enough to preserve context.
Key Concepts
- Chunk Size: Target size for each text chunk (in characters or tokens)
- Chunk Overlap: Number of characters/tokens to overlap between chunks (preserves context)
- Separators: Characters used to split text (newlines, periods, spaces)
- Metadata: Preserved and enriched during splitting (including start_index)
Splitter Selection Decision Table
| Splitter | Best For | Package | Key Features |
|---|
| RecursiveCharacterTextSplitter | General purpose | @langchain/textsplitters | Hierarchical splitting, preserves structure |
| CharacterTextSplitter | Simple splitting | @langchain/textsplitters | Split by single separator |
| TokenTextSplitter | Token-aware splitting | @langchain/textsplitters | Counts actual tokens, not characters |
| MarkdownTextSplitter | Markdown documents | @langchain/textsplitters | Preserves markdown structure |
| RecursiveJsonSplitter | JSON data | @langchain/textsplitters | Splits JSON while preserving structure |
When to Choose Each Splitter
Choose RecursiveCharacterTextSplitter if:
- You're working with general text (default choice)
- You want to preserve natural text structure
- You need balanced chunks
Choose TokenTextSplitter if:
- You need precise token counts for model limits
- Character counts are unreliable for your use case
Choose MarkdownTextSplitter if:
- You're processing markdown documentation
- You want to preserve headers and structure
Code Examples
RecursiveCharacterTextSplitter (Recommended)
import { RecursiveCharacterTextSplitter } from "@langchain/textsplitters";
const splitter = new RecursiveCharacterTextSplitter({
chunkSize: 1000,
chunkOverlap: 200,
});
const text = "Long document text here...";
const chunks = await splitter.splitText(text);
console.log(`Created ${chunks.length} chunks`);
chunks.forEach((chunk, i) => {
console.log(`Chunk ${i + 1}: ${chunk.length} characters`);
});
import { Document } from "@langchain/core/documents";
const docs = [
new Document({
pageContent: "Long text...",
metadata: { source: "doc1.pdf", page: 1 }
})
];
const splitDocs = await splitter.splitDocuments(docs);
How RecursiveCharacterTextSplitter Works
const splitter = new RecursiveCharacterTextSplitter({
chunkSize: 1000,
chunkOverlap: 200,
separators: ["\n\n", "\n", " ", ""],
});
CharacterTextSplitter (Simple)
import { CharacterTextSplitter } from "@langchain/textsplitters";
const splitter = new CharacterTextSplitter({
separator: "\n\n",
chunkSize: 1000,
chunkOverlap: 200,
});
const chunks = await splitter.splitText(text);
TokenTextSplitter (Token-Aware)
import { TokenTextSplitter } from "@langchain/textsplitters";
const splitter = new TokenTextSplitter({
chunkSize: 512,
chunkOverlap: 50,
encodingName: "cl100k_base",
});
const chunks = await splitter.splitText(text);
MarkdownTextSplitter
import { RecursiveCharacterTextSplitter } from "@langchain/textsplitters";
const splitter = RecursiveCharacterTextSplitter.fromLanguage("markdown", {
chunkSize: 1000,
chunkOverlap: 200,
});
const markdown = `
# Header 1
Some content under header 1.
## Header 2
Content under header 2.
`;
const chunks = await splitter.splitText(markdown);
Splitting Long Documents
import { RecursiveCharacterTextSplitter } from "@langchain/textsplitters";
import { PDFLoader } from "@langchain/community/document_loaders/fs/pdf";
const loader = new PDFLoader("large-document.pdf");
const docs = await loader.load();
const splitter = new RecursiveCharacterTextSplitter({
chunkSize: 1000,
chunkOverlap: 200,
});
const splitDocs = await splitter.splitDocuments(docs);
console.log(`${docs.length} pages split into ${splitDocs.length} chunks`);
splitDocs.forEach(chunk => {
console.log(chunk.metadata);
});
Code Splitting
import { RecursiveCharacterTextSplitter } from "@langchain/textsplitters";
const jsSplitter = RecursiveCharacterTextSplitter.fromLanguage("js", {
chunkSize: 500,
chunkOverlap: 50,
});
const pythonSplitter = RecursiveCharacterTextSplitter.fromLanguage("python", {
chunkSize: 500,
chunkOverlap: 50,
});
Custom Separators
import { RecursiveCharacterTextSplitter } from "@langchain/textsplitters";
const splitter = new RecursiveCharacterTextSplitter({
chunkSize: 1000,
chunkOverlap: 100,
separators: [
"\n\n\n",
"\n\n",
"\n",
". ",
" ",
"",
],
});
Splitting with Vector Store Integration
import { RecursiveCharacterTextSplitter } from "@langchain/textsplitters";
import { MemoryVectorStore } from "langchain/vectorstores/memory";
import { OpenAIEmbeddings } from "@langchain/openai";
import { CheerioWebBaseLoader } from "@langchain/community/document_loaders/web/cheerio";
const loader = new CheerioWebBaseLoader("https://docs.example.com");
const docs = await loader.load();
const splitter = new RecursiveCharacterTextSplitter({
chunkSize: 1000,
chunkOverlap: 200,
});
const splitDocs = await splitter.splitDocuments(docs);
const vectorStore = await MemoryVectorStore.fromDocuments(
splitDocs,
new OpenAIEmbeddings()
);
const results = await vectorStore.similaritySearch("query", 4);
Boundaries
What Agents CAN Do
✅ Split text intelligently
- Use recursive splitting to preserve structure
- Configure chunk size and overlap
- Choose appropriate separators
✅ Handle various formats
- Plain text, markdown, code
- Documents with metadata
- JSON and structured data
✅ Optimize for use case
- Balance chunk size vs context
- Adjust overlap for continuity
- Use token-based splitting for models
✅ Integrate with pipelines
- Combine with loaders and vector stores
- Preserve metadata through splitting
What Agents CANNOT Do
❌ Guarantee semantic boundaries
- Splitters use heuristics, not perfect semantic understanding
- May split mid-sentence in edge cases
❌ Perfectly estimate tokens
- Character-based splitters approximate tokens
- Use TokenTextSplitter for exact counts
❌ Split without losing some context
- Even with overlap, some context may be lost
- Trade-off between chunk size and context
Gotchas
1. Chunk Size vs Token Limits
const splitter = new RecursiveCharacterTextSplitter({
chunkSize: 4000,
});
import { TokenTextSplitter } from "@langchain/textsplitters";
const splitter = new TokenTextSplitter({
chunkSize: 4000,
encodingName: "cl100k_base",
});
Fix: Use TokenTextSplitter when token precision matters.
2. Too Small Chunks Lose Context
const splitter = new RecursiveCharacterTextSplitter({
chunkSize: 100,
chunkOverlap: 0,
});
const splitter = new RecursiveCharacterTextSplitter({
chunkSize: 1000,
chunkOverlap: 200,
});
Fix: Use 500-2000 characters with 10-20% overlap for most cases.
3. Zero Overlap Breaks Continuity
const splitter = new RecursiveCharacterTextSplitter({
chunkSize: 1000,
chunkOverlap: 0,
});
const splitter = new RecursiveCharacterTextSplitter({
chunkSize: 1000,
chunkOverlap: 200,
});
Fix: Always use overlap (typically 10-20% of chunk size).
4. Metadata Not Preserved
const chunks = await splitter.splitText(documentText);
const docs = [new Document({
pageContent: documentText,
metadata: { source: "file.pdf" }
})];
const chunks = await splitter.splitDocuments(docs);
Fix: Use splitDocuments() instead of splitText() to keep metadata.
Links and Resources
Official Documentation
Package Installation
npm install @langchain/textsplitters