# Generate a QR code in Rust

> The qrcode crate encodes and the image crate saves: QrCode::new(b"data"), then .render::<Luma<u8>>().build() and .save("qr.png"). It also renders SVG and terminal strings natively. For styled codes without renderer code, fetch the keyless API with reqwest and write the bytes.

Source: https://useqr.app/docs/developers/generate-a-qr-code-in-rust · Last reviewed 2026-08-21 · UseQR is free forever, MIT licensed, no signup.

---

## The qrcode crate

`cargo add qrcode image` and the canonical usage is four lines:

```rust
use image::Luma;
use qrcode::QrCode;

fn main() {
    let code = QrCode::new(b"https://example.com").unwrap();
    let image = code.render::<Luma<u8>>().min_dimensions(512, 512).build();
    image.save("qr.png").unwrap();
}
```

`render::<Luma<u8>>()` produces a greyscale `image` crate buffer; `min_dimensions` scales
the modules up to at least the requested size while keeping them integer-sized, so edges
stay sharp. The renderer includes the 4-module [quiet zone](/glossary/quiet-zone) by
default.

Error correction is set at construction:

```rust
use qrcode::{EcLevel, QrCode};

let code = QrCode::with_error_correction_level(b"https://example.com", EcLevel::H).unwrap();
```

`EcLevel::L`, `M`, `Q`, `H` are the standard
[error-correction levels](/docs/spec/error-correction-levels-explained).

## SVG and terminal output, no extra crates

The same crate renders SVG:

```rust
use qrcode::render::svg;

let image = code
    .render()
    .min_dimensions(512, 512)
    .dark_color(svg::Color("#000000"))
    .light_color(svg::Color("#ffffff"))
    .build();
std::fs::write("qr.svg", image).unwrap();
```

SVG is what you want for [print](/docs/print/qr-code-size-for-print). And for CLI tools,
`code.render::<char>().build()` returns a string you can `println!` — a scannable code in
the terminal with zero image handling.

## The zero-dependency-logic route: reqwest

For styling beyond two colours, typed payloads (WiFi, UPI, vCard) or when you would rather
not own encoding logic at all, fetch the
[keyless API](/docs/developers/free-qr-code-api-no-key):

```rust
fn main() -> Result<(), Box<dyn std::error::Error>> {
    let url = format!(
        "https://useqr.app/api/v1/qr?data={}&size=1024&ec=Q",
        urlencoding::encode("https://example.com/sale?src=poster")
    );
    let bytes = reqwest::blocking::get(&url)?.bytes()?;
    std::fs::write("qr.png", &bytes)?;
    Ok(())
}
```

Percent-encoding the payload is mandatory — a raw `&` starts a new query parameter and
truncates your data with no error. In async code, the same call is
`reqwest::get(&url).await?.bytes().await?`.

## Verify what you generated

Rendering proves nothing about scannability once colours or logos enter the picture. One
GET closes the loop:

```rust
#[derive(serde::Deserialize)]
struct Report { scannable: bool, issues: Vec<String> }

let report: Report = reqwest::blocking::get(
    "https://useqr.app/api/v1/verify?data=hello&color=cccccc"
)?.json()?;
assert!(report.scannable, "{:?}", report.issues);
```

The endpoint renders, rasterises and decodes with a real decoder — the
[decode-verify loop](/docs/developers/why-verify-that-your-qr-code-decodes) as a service.
How the Rust crate compares with other ecosystems' libraries is in
[QR code libraries compared](/docs/developers/qr-code-libraries-compared).

## FAQ

### What is the standard Rust crate for QR codes?
The qrcode crate, paired with the image crate for PNG output. It also renders SVG and terminal strings without additional dependencies.

### How do I set the error correction level in Rust?
Use QrCode::with_error_correction_level with EcLevel::L, M, Q or H instead of QrCode::new. H survives the most damage at the cost of a denser code.

### How do I get sharp, non-blurry output?
Use min_dimensions on the renderer so modules scale to whole pixels, and save PNG or SVG. Avoid resampling the finished raster afterwards.

### Can I generate QR codes in Rust without any crates?
Not realistically — but you can skip encoding logic entirely by fetching the keyless HTTP API and writing the returned bytes to disk.

## Try it

- https://useqr.app/url
- https://useqr.app/json
- https://useqr.app/validate
