Open-source project
mrdimidium/nanoid avatar
mrdimidium/nanoid

nanoid: 21 characters of URL-friendly randomness from a Rust macro

A tiny, secure, URL-friendly, unique string ID generator for Rust

620 stars40 forksRustMIT

At a glance

What is it?
nanoid is a Rust crate that generates short, URL-friendly unique IDs from a cryptographically strong random source, with a macro that also accepts a custom length, alphabet or random generator. It is a small, focused library, and its trade is that 21 characters are not universally unique in the way a UUID claims to be.
Who is it for?
Adopt nanoid when you want short, collision resistant, URL-safe identifiers in Rust and you are willing to reason about the collision math for your own data volume, because 21 characters drawn from the 64 symbol alphabet is a probabilistic guarantee rather than a formal one. Do not adopt it where a UUID type is expected by a downstream system, or where you need identifiers that are reproducible from a seed in production, since the seeded generator exists for tests.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 73 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The whole API is a macro and a 64 symbol alphabet

nanoid is a Rust crate for generating unique string IDs that are short enough to sit in a URL without escaping. The main module uses the URL-friendly symbols A-Za-z0-9_- and returns an ID of 21 characters. The usage example in the README is four lines long, which is close to the entire surface a normal caller touches:

rust
use nanoid::nanoid;

fn main() {
   let id = nanoid!(); //=> "Yo1Tr9F3iF-LFHX9i9GvA"
}

The result is a string, not a typed wrapper. That matters for how it fits into a codebase, because a nanoid drops into any field that takes a String without an adapter, and equally because nothing in the type system tells you later that a given string came from this generator.

The claim of compactness is made against UUID directly. The README states that the crate uses a larger alphabet than UUID and reaches a similar number of possible IDs in 21 symbols instead of 36. The reasoning is straightforward arithmetic: 64 symbols to the power of 21 gives a larger space than 16 symbols to the power of 36, and you get there in fewer characters.

Adding the dependency is one line in Cargo.toml

The install step is documented in the README under a heading called Install, and it is a Cargo dependency entry:

toml
[dependencies]
nanoid = "0.5.0"

The version in that snippet matches the version in the repository manifest, so the documented snippet and the crate are in step. The manifest also records the package as MIT licensed, with edition 2018, and it names the crate nanoid with the description A tiny, secure, URL-friendly, unique string ID generator for Rust.

The dependency footprint is small and worth noting. The runtime dependency list is rand at version 0.9, plus two optional crates, compact_str and smartstring, which are not enabled by default because the features section sets default to an empty list. The optional crates exist to let the crate return a more compact string type when the caller wants one, and leaving them out keeps the build lean. Dev dependencies are criterion, which is what the bench named rngs uses, and doc-comment.

The crate documentation lives on docs.rs rather than in the repository, and the repository top level is small: src, examples, benches, a Cargo.toml and a CHANGELOG.md.

Length and alphabet are macro arguments, not separate functions

Shorter IDs are a macro argument. The README describes this as reducing the ID length and increasing the collision probability, and the example passes a number:

rust
use nanoid::nanoid;

fn main() {
   let id = nanoid!(10); //=> "IRFa-VaY2b"
}

The alphabet is the second argument, and the README example builds a 16 character array of digits and lowercase letters:

rust
use nanoid::nanoid;

fn main() {
    let alphabet: [char; 16] = [
        '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'a', 'b', 'c', 'd', 'e', 'f'
    ];

   let id = nanoid!(10, &alphabet); //=> "4f90d13a42"
}

There is a constraint on that alphabet, and it is a security constraint rather than a stylistic one. The README states that the alphabet must contain 256 symbols or less, and that otherwise the generator will not be secure. The reasoning is that the generator maps random bytes onto alphabet positions, so an alphabet with more entries than the byte range can represent would not be reachable evenly.

The default alphabet is also exposed under nanoid::alphabet::SAFE, which is what you want when you keep the default symbols but change where the randomness comes from.

Replacing the random source is the escape hatch for tests

The third argument to the macro is your own random byte generator, and the README frames it as replacing the default safe generator. The example builds a function that fills a vector of bytes and passes it in with a small alphabet:

rust
use nanoid::nanoid;

fn random_byte () -> u8 { 0 }

fn main() {
    fn random (size: usize) -> Vec<u8> {
        let mut bytes: Vec<u8> = vec![0; size];

        for i in 0..size {
            bytes[i] = random_byte();
        }

        bytes
    }

    nanoid!(10, &['a', 'b', 'c', 'd', 'e', 'f'], random); //=> "fbaefaadeb"
}

The contract is narrow: the function must accept the array size and return a vector of random bytes. The README states that the generator accepts Fn and FnMut closures, and lists the consequences as seeded random generators for reproducible IDs, custom stateful generators, and integration with external random sources.

The seeded variant is the one most teams will reach for. It uses rand's StdRng with a fixed seed, and the README describes the purpose as reproducible output for testing or when deterministic output is needed:

rust
use nanoid::nanoid;
use rand::{rngs::StdRng, Rng, SeedableRng};

fn main() {
    let mut rng = StdRng::seed_from_u64(42);

    let id = nanoid!(10, &nanoid::alphabet::SAFE, |size| {
        let mut bytes = vec![0u8; size];
        rng.fill(&mut bytes[..]);
        bytes
    });

    println!("{}", id); //=> "wyBwxRa4Xf"
}

The catch is obvious once you see it: a seeded generator produces the same IDs every run, which is exactly what a test wants and exactly what a production database must never do. The macro does not warn you, so the responsibility for keeping the two paths apart sits with the caller.

URL friendliness has one documented edge case

The README is unusually precise about a detail that most ID libraries leave vague. It notes that the symbols -, . and ( are not encoded in a URL, and that if used at the end of a link they could be identified as a punctuation symbol.

So an ID can end in a character that a reader might mistake for part of the surrounding prose. The generated example in the README, Yo1Tr9F3iF-LFHX9i9GvA, contains exactly that kind of hyphen. The problem is cosmetic in most cases, but it becomes a real one when an ID is the last thing in a path segment, a filename, or a printed identifier that a person will read aloud or type back in.

The mitigation available to you is the alphabet argument. Choosing an alphabet without the ambiguous symbols removes the case at the cost of a smaller space for the same ID length.

The README also points out that the crate was ported to many languages, with a reference implementation in JavaScript and ports for C, C#, C++, Go, Java, Kotlin, Python, Ruby, Swift, Zig and others. The stated reason for the ports is to have the same ID generator on the client and the server side, which matters if a client mints an identifier that the server later has to recognise.

21 characters is a probabilistic guarantee, not a formal one

The crate calls itself safe because it uses cryptographically strong random APIs and guarantees a proper distribution of symbols. Both halves of that claim are worth separating, because they carry different weight.

The distribution part is a real property and it is the one that the alphabet limit of 256 symbols protects. The randomness part comes from the rand crate at version 0.9, so the quality of the underlying bytes is rand's responsibility rather than nanoid's, and the strength of the guarantee is the strength of the platform random source behind it.

The unbounded scope of the claim is about uniqueness, and here the honest framing is probabilistic. A 21 character ID over 64 symbols has a very large space, and collisions become relevant only at data volumes far beyond a single database. But a UUID is usually specified as unique by construction, with a version and variant embedded in the layout, so code that expects a UUID will not accept a nanoid string without conversion.

That is the practical limit to keep in mind. nanoid is a random string generator with excellent statistical properties, not a globally unique identifier type, and the README never claims otherwise.

Maintenance is quiet and versioned on crates.io only

The repository is not archived, and the last push was on 2026-08-12. The project publishes no GitHub releases, so the version history lives in the CHANGELOG.md at the root and in the crates.io listing, and the manifest version is 0.5.0.

That the crate sits at 0.5.0 after a while in development tells you the author is not treating it as a finished API, and a pre-1.0 version number in Rust carries a real meaning: minor versions may break compatibility. The README and manifest also still use edition 2018, which is two editions behind the current default, and the dependency on rand is at 0.9, so the crate is building on a fairly recent rand.

The optional compact_str and smartstring dependencies point to a design that is still in progress. Choosing between a compact string type and a smart string type as the return representation is a decision that will change how the API feels to use, and the fact that both are behind feature flags rather than settled suggests the author has not decided yet.

The examples directory holds complex.rs, fnmut.rs and macros.rs, and the benches directory holds a criterion benchmark named rngs, so the performance side of the generator is at least measured even if the results are not published in the README.

Editorial conclusion

Adopt nanoid when you want short, collision resistant, URL-safe identifiers in Rust and you are willing to reason about the collision math for your own data volume, because 21 characters drawn from the 64 symbol alphabet is a probabilistic guarantee rather than a formal one. Do not adopt it where a UUID type is expected by a downstream system, or where you need identifiers that are reproducible from a seed in production, since the seeded generator exists for tests. Before you rely on it, check the version you pull, since the manifest declares 0.5.0 with edition 2018 and rand 0.9, and the repository publishes no GitHub releases, so crates.io is the only place the version history is recorded.

Frequently asked questions

How do I use nanoid in Rust?

Add `nanoid = "0.5.0"` to your Cargo.toml dependencies, then call the macro: `let id = nanoid!();` returns a 21 character ID using the URL-friendly symbols A-Za-z0-9_-.

Can I generate shorter nanoid IDs or use a different alphabet?

Yes. The length is the first macro argument, so `nanoid!(10)` returns a 10 character ID, and the alphabet is the second, so `nanoid!(10, &alphabet)` uses your own symbol set. The alphabet must contain 256 symbols or less, otherwise the generator will not be secure.

How do I get reproducible nanoid values in tests?

Pass your own random byte generator as the third macro argument. The README shows a seeded variant using rand's StdRng with seed_from_u64, which produces the same ID on every run, and the generator may be an Fn or an FnMut closure.

Is nanoid as unique as a UUID?

The README frames the claim as similar rather than identical: it uses a larger alphabet than UUID and reaches a similar number of possible IDs in 21 symbols instead of 36. The guarantee is statistical, based on cryptographically strong random APIs and a proper distribution of symbols, not uniqueness by construction.

What licence is the nanoid crate under?

MIT. The repository manifest records the licence as MIT, and the repository root contains the LICENSE file.

Official sources

  1. Issues
  2. License: MIT
  3. mrdimidium/nanoid on GitHub
  4. Project website
  5. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/mrdimidium-nanoid.svg)](https://hysenlabs.com/projects/mrdimidium-nanoid)

Was this useful?