text-splitter

Library Featured

Split text into semantic chunks, up to a desired chunk size. Supports calculating length by characters and tokens, and is callable from Rust and Python.

Checked · Sources

Downloads
271,174
Dependents
56
Stars
645
Forks
35
Contributors
8
Commits
1,396
Latest release
0.33.1 ·

About

From the project README ·

Large language models (LLMs) can be used for many tasks, but often have a limited context size that can be smaller than documents you might want to use. To use documents of larger length, you often have to split your text into chunks to fit within this context size.

This crate provides methods for splitting longer pieces of text into smaller chunks, aiming to maximize a desired chunk size, but still splitting at semantically sensible boundaries whenever possible.

Get Started

Add it to your project with

cargo add text-splitter
By Number of Characters

The simplest way to use this crate is to use the default implementation, which uses character count for chunk size.

use text_splitter::TextSplitter;

// Maximum number of characters in a chunk
let max_characters = 1000;
// Default implementation uses character count for chunk size
let splitter = TextSplitter::new(max_characters);

let chunks = splitter.chunks("your document text");
Read the full README
With Hugging Face Tokenizer

Requires the tokenizers feature to be activated and adding tokenizers to dependencies. The example below, using from_pretrained(), also requires tokenizers http feature to be enabled.

cargo add text-splitter --features tokenizers
cargo add tokenizers --features http
use text_splitter::{ChunkConfig, TextSplitter};
// Can also use anything else that implements the ChunkSizer
// trait from the text_splitter crate.
use tokenizers::Tokenizer;

let mut tokenizer = Tokenizer::from_pretrained("bert-base-cased", None).unwrap();
// If your tokenizer has truncation enabled, disable it before passing it to
// the splitter. Otherwise chunk sizes can be capped by the tokenizer's
// truncation limit.
tokenizer.with_truncation(None).unwrap();

let max_tokens = 1000;
let splitter = TextSplitter::new(ChunkConfig::new(max_tokens).with_sizer(tokenizer));

let chunks = splitter.chunks("your document text");
With Tiktoken Tokenizer

Requires the tiktoken-rs feature to be activated and adding tiktoken-rs to dependencies.

cargo add text-splitter --features tiktoken-rs
cargo add tiktoken-rs
use text_splitter::{ChunkConfig, TextSplitter};
// Can also use anything else that implements the ChunkSizer
// trait from the text_splitter crate.
use tiktoken_rs::cl100k_base;

let tokenizer = cl100k_base().unwrap();
let max_tokens = 1000;
let splitter = TextSplitter::new(ChunkConfig::new(max_tokens).with_sizer(tokenizer));

let chunks = splitter.chunks("your document text");
Using a Range for Chunk Capacity

You also have the option of specifying your chunk capacity as a range.

Once a chunk has reached a length that falls within the range it will be returned.

It is always possible that a chunk may be returned that is less than the start value, as adding the next piece of text may have made it larger than the end capacity.

use text_splitter::{ChunkConfig, TextSplitter};

// Maximum number of characters in a chunk. Will fill up the
// chunk until it is somewhere in this range.
let max_characters = 500..2000;
// Default implementation uses character count for chunk size
let splitter = TextSplitter::new(max_characters);

let chunks = splitter.chunks("your document text");
Markdown

All of the above examples also can also work with Markdown text. If you enable the markdown feature, you can use the MarkdownSplitter in the same ways as the TextSplitter.

cargo add text-splitter --features markdown
use text_splitter::MarkdownSplitter;
// Maximum number of characters in a chunk. Can also use a range.
let max_characters = 1000;
// Default implementation uses character count for chunk size.
// Can also use all of the same tokenizer implementations as `TextSplitter`.
let splitter = MarkdownSplitter::new(max_characters);

let chunks = splitter.chunks("# Header\n\nyour document text");
Code

All of the above examples also can also work with code that can be parsed with tree-sitter. If you enable the code feature, you can use the CodeSplitter in the same ways as the TextSplitter.

cargo add text-splitter --features code
cargo add tree-sitter-<language>
use text_splitter::CodeSplitter;
// Maximum number of characters in a chunk. Can also use a range.
let max_characters = 1000;
// Default implementation uses character count for chunk size.
// Can also use all of the same tokenizer implementations as `TextSplitter`.
let splitter = CodeSplitter::new(tree_sitter_rust::LANGUAGE, max_characters).expect("Invalid tree-sitter language");

let chunks = splitter.chunks("your code file");

Method

To preserve as much semantic meaning within a chunk as possible, each chunk is composed of the largest semantic units that can fit in the next given chunk. For each splitter type, there is a defined set of semantic levels. Here is an example of the steps used:

  1. Split the text by a increasing semantic levels.
  2. Check the first item for each level and select the highest level whose first item still fits within the chunk size.
  3. Merge as many of these neighboring sections of this level or above into a chunk to maximize chunk length. Boundaries of higher semantic levels are always included when merging, so that the chunk doesn't inadvertantly cross semantic boundaries.

The boundaries used to split the text if using the chunks method, in ascending order:

TextSplitter Semantic Levels
  1. Characters
  2. Unicode Grapheme Cluster Boundaries
  3. Unicode Word Boundaries
  4. Unicode Sentence Boundaries
  5. Ascending sequence length of newlines. (Newline is \r\n, \n, or \r) Each unique length of consecutive newline sequences is treated as its own semantic level. So a sequence of 2 newlines is a higher level than a sequence of 1 newline, and so on.

Splitting doesn't occur below the character level, otherwise you could get partial bytes of a char, which may not be a valid unicode str.

MarkdownSplitter Semantic Levels

Markdown is parsed according to the CommonMark spec, along with some optional features such as GitHub Flavored Markdown.

  1. Characters
  2. Unicode Grapheme Cluster Boundaries
  3. Unicode Word Boundaries
  4. Unicode Sentence Boundaries
  5. Soft line breaks (single newline) which isn't necessarily a new element in Markdown.
  6. Inline elements such as: text nodes, emphasis, strong, strikethrough, link, image, table cells, inline code, footnote references, task list markers, and inline html.
  7. Block elements suce as: paragraphs, code blocks, footnote definitions, metadata. Also, a block quote or row/item within a table or list that can contain other "block" type elements, and a list or table that contains items.
  8. Thematic breaks or horizontal rules.
  9. Headings by level

Splitting doesn't occur below the character level, otherwise you could get partial bytes of a char, which may not be a valid unicode str.

CodeSplitter Semantic Levels
  1. Characters
  2. Unicode Grapheme Cluster Boundaries
  3. Unicode Word Boundaries
  4. Unicode Sentence Boundaries
  5. Ascending depth of the syntax tree. So function would have a higher level than a statement inside of the function, and so on.

Splitting doesn't occur below the character level, otherwise you could get partial bytes of a char, which may not be a valid unicode str.

Note on sentences

There are lots of methods of determining sentence breaks, all to varying degrees of accuracy, and many requiring ML models to do so. Rather than trying to find the perfect sentence breaks, we rely on unicode method of sentence boundaries, which in most cases is good enough for finding a decent semantic breaking point if a paragraph is too large, and avoids the performance penalties of many other methods.

Feature Flags

Document Format Support
FeatureDescription
codeEnables the CodeSplitter struct for parsing code documents via tree-sitter parsers.
markdownEnables the MarkdownSplitter struct for parsing Markdown documents via the CommonMark spec.
Tokenizer Support
Dependency FeatureVersion SupportedDescription
tiktoken-rs^0.12.0Enables (Text/Markdown)Splitter::new to take tiktoken_rs::CoreBPE as an argument. This is useful for splitting text for OpenAI models.
tokenizers^0.23.0Enables (Text/Markdown)Splitter::new to take tokenizers::Tokenizer as an argument. This is useful for splitting text models that have a Hugging Face-compatible tokenizer.

Inspiration

This crate was inspired by LangChain's TextSplitter. But, looking into the implementation, there was potential for better performance as well as better semantic chunking.

A big thank you to the Unicode team for their icu_segmenter crate that manages a lot of the complexity of matching the Unicode rules for words and sentences.

Runs on
GPU support unknown
What shows it
  • no dependency or feature matches a backend row
Native code
Optional
  • Binds native code: tree-sitter (feature code), tree-sitter-language (feature code)
Python bindings
No Python binding crate found
License
MIT

Compare with similar projects

Order: strongest tier first, then most downloads, then name, with unmeasured values last. A library counts downloads over 30 days. An app counts binary downloads per release. An app with none counts its crate downloads over 30 days. ML Rust runs this catalogue, so its closest project always gets a row.

text-splitter beside the projects that share a topic with it
ProjectDownloadsDependentsStarsLatest releaseRuns on (GPU)Native codeLicense
text-splitter This page Featured

Split text into semantic chunks, up to a desired chunk size. Supports calculating length by characters and tokens, and is callable from Rust and Python.

271,174 downloads over 30 days 56 645 0.33.1 GPU support unknown Binds native code: tree-sitter (feature code), tree-sitter-language (feature code) MIT
tiktoken-rs Featured

Library for encoding and decoding with the tiktoken library in Rust

3,939,182 downloads over 30 days 449 410 0.12.1 GPU support unknown No native code found MIT
splintr Listed

Tokenizer for Rust and Python: pure Rust by default, with 18 bundled vocabularies and four backends covering BPE, SentencePiece and WordPiece. Its output matches Hugging Face tokenizers id for id on 14 model families, including GPT-2, Qwen, Whisper, T5 and BERT.

Python bindings

8,204 downloads over 30 days 7 63 0.22.2 GPU support unknown Binds native code: pcre2-sys (feature pcre2) MIT
semchunk-rs Listed Dormant

A fast and lightweight Rust library for splitting text into semantically meaningful chunks.

864 downloads over 30 days 3 4 0.1.1 GPU support unknown Binds native code: torch-sys (feature rust-bert) MIT
xberg New

High-performance document intelligence library for Rust. Extract text, metadata, and structured data from PDFs, Office documents, images, and 107 formats and 371 programming languages via tree-sitter code intelligence with async/sync APIs.

26,095 downloads over 30 days 2 9,565 1.3.10 CUDA, Metal Binds native code: libheif-sys, libz-sys, xberg_libwpd, xberg_tesseract, clipper-sys and 4 more. Build uses a native toolchain: cc, bindgen (feature bindings), cmake (feature build-tesseract), pkg-config (feature dynamic-linking), vcpkg (feature dynamic-linking) MIT

From-scratch pure-Rust SentencePiece inference (encode/decode), differentially verified against the upstream C++/Python implementation.

8,442 downloads over 30 days 3 1 0.1.1 GPU support unknown No native code found Apache-2.0
Why FeaturedIt passes every Featured gate.

What it meets

  • It has 41 active dependents and 32 new dependents in 365 days. Featured needs 11 active dependents or 9 new dependents in 365 days.
  • Active dependents are at 100% of their peak. Featured needs at least 50%.
  • Docs are present. The release commit or a manifest ties the crate to the repository.
  • No vulnerability advisory stays unpatched past the grace window. At least one version is neither a prerelease nor 0.0.x.
  • The last activity is on 26 Oct 2026, 14 days ago. The rules expect an update within 1200 days. Dormancy rule

Exact rules: Featured library. The check on applied thresholds version 9.

Every rule outcome, as the check records it
  • expected-interval: version 0.33.1 base 300, doubled for 271174 downloads per month, doubled for 41 active users: E = 1200 days
  • dormancy: last activity 2026-10-26, 14 days ago, E = 1200: active
  • featured: featured: active_users 41, new_adopters 32, former_glory 1

Measurements and sources

Each signal row shows the raw value, its source and the fetch date. The Sources list gathers every distinct source on this page. Movement needs more history.

Sources6 sources
Adoption41 active dependents, 32 new dependents in 365 days1 not measured
Adoption measurements
SignalValueSourceFetchedCounts
Active dependents41crates.io database dumpYes
New dependents (365 days)32crates.io database dumpYes
Peak active dependents40.5crates.io database dumpYes
Active dependents as a share of the peak1crates.io database dumpYes
Binary downloads per releaseNot measuredYes
MaintenanceLatest human commit: 14 days ago3 not measured
Maintenance measurements
SignalValueSourceFetchedCounts
Latest human commit2026-10-26GitHub repositoryYes
Latest release2026-10-25crates.io database dumpYes
First release2023-05-05crates.io database dumpYes
Latest version0.33.1crates.io database dumpYes
Days the oldest outside issue waits for a reply476GitHub repositoryYes
Maintainer replies over 365 days3GitHub repositoryYes
Repository archivedfalseGitHub repositoryYes
Repository gonefalseGitHub repositoryYes
Release commit or manifest in the repotruecrates.io packageYes
Published docstruecrates.io database dumpYes
Latest release fails cargo checkNot measuredYes
Build check dateNot measuredYes
Build check toolchainNot measuredYes
SecurityUnpatched vulnerabilities: none
Security measurements
SignalValueSourceFetchedCounts
Unpatched vulnerabilities past the grace windownoneRustSec advisory databaseYes
RustSec unmaintained advisorynoneRustSec advisory databaseYes
Deprecation noticenonecrates.io database dumpYes
Deprecated requirementnonecrates.io database dumpYes
Distribution65 releases, 271,174 downloads over 30 days5 not measured
Distribution measurements
SignalValueSourceFetchedCounts
Releases65crates.io database dumpYes
Downloads over 30 days271174crates.io database dumpYes
Binary downloads over the last 3 releasesNot measuredYes
Homebrew installs on request over 365 daysNot measuredYes
Repology repository familiesNot measuredYes
Docker pull growth over 90 daysNot measuredYes
Docker pulls at the growth baselineNot measuredYes
Every version a prereleasefalsecrates.io database dumpYes
Every version 0.0.xfalsecrates.io database dumpYes
Every version yankedfalsecrates.io database dumpYes
Crate deleted from crates.iofalsecrates.io database dumpYes
Repository645 stars, 35 forks, 8 contributors, 1,396 commits. Stars alone never qualify a project.
Repository measurements
SignalValueSourceFetchedCounts
GitHub stars645GitHub repositoryYes — one part of the repository route
GitHub forks35GitHub repositoryYes — one part of the repository route
Contributors with a GitHub account8GitHub contributorsYes — one part of the repository route
Commits on the default branch1396GitHub repositoryNo
Repository's main crate left out of the entrynonecrates.io database dumpYes
Facts that never count
Facts that never count measurements
SignalValueSourceFetchedCounts
LicenseMITcrates.io database dumpNo
Runs on, from listed crates and their direct dependenciesGPU support unknowncrates.io database dumpNo
What shows each backendno dependency or feature matches a backend rowcrates.io database dumpNo
Native code, from listed crates and their dependenciesBinds native code: tree-sitter (feature code), tree-sitter-language (feature code)crates.io database dumpNo
Python bindings, from direct dependencies of listed cratesNo Python binding crate foundcrates.io database dumpNo
Crates that depend on a listed crate directly56crates.io database dumpNo

Corrections and review history

Report a correction

No correction applies to this entry.

Judgments

A judgment enters only through an approved issue and a merged pull request.

No judgment applies to this entry.