Using Rust Crates in Tauri: regex, chrono & uuid | Lesson 73
Video: Using Rust Crates in Tauri: regex, chrono & uuid | Lesson 73 by Taught by Celeste AI - AI Coding Coach
The Rust ecosystem is your standard library. Add a crate, expose a Tauri command, call it from React. The pattern that makes Tauri's backend feel limitless.
One of the underrated wins of Tauri is that your backend is just Rust. Anything on crates.io — regex, chrono, uuid, sqlx, reqwest, image, ring, base64, anything — works in your Tauri app with one cargo add and a few lines of glue.
Today we add three small crates and expose four commands that use them.
What we are building
Four Tauri commands powered by three crates:
- regex: extract email addresses from a block of text.
- chrono: get the current time and format dates.
- uuid: generate a random UUID.
All exposed as #[tauri::command] so the React frontend can call them with invoke.
Adding the crates
cd src-tauri
cargo add regex chrono uuid --features uuid/v4
cargo add writes to your Cargo.toml. After this:
[dependencies]
tauri = { version = "2", features = [] }
serde = { version = "1", features = ["derive"] }
regex = "1"
chrono = "0.4"
uuid = { version = "1", features = ["v4"] }
The --features uuid/v4 enables UUID v4 generation specifically. Many crates have feature flags that pull in optional functionality.
Command 1: extract emails
use regex::Regex;
#[tauri::command]
fn extract_emails(text: String) -> Vec<String> {
let re = Regex::new(r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}").unwrap();
re.find_iter(&text)
.map(|m| m.as_str().to_string())
.collect()
}
Regex::new compiles the pattern. .find_iter returns every match. We collect the matches into a Vec<String>.
For real apps, lazy-static the regex:
use once_cell::sync::Lazy;
static EMAIL_RE: Lazy<Regex> = Lazy::new(|| {
Regex::new(r"...").unwrap()
});
So the regex is only compiled once, not per call.
Command 2: current time
use chrono::Local;
#[tauri::command]
fn get_current_time() -> String {
Local::now().format("%Y-%m-%d %H:%M:%S").to_string()
}
Local::now() returns the current local time as a DateTime<Local>. .format accepts a strftime-style string.
For UTC, use chrono::Utc::now(). For a specific timezone, the chrono-tz crate.
Command 3: format a date
#[tauri::command]
fn format_date(format_str: String) -> Result<String, String> {
let now = Local::now();
let formatted = now.format(&format_str).to_string();
if formatted.is_empty() {
return Err("Invalid format string".to_string());
}
Ok(formatted)
}
User-supplied format string. Returns Result so a bad format reaches the frontend as an error.
Command 4: generate a UUID
use uuid::Uuid;
#[tauri::command]
fn generate_uuid() -> String {
Uuid::new_v4().to_string()
}
Uuid::new_v4() produces a random v4 UUID. .to_string() formats it as xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx.
UUID has many variants: v1 (timestamp + MAC), v3/v5 (namespace + name hashed), v4 (random), v7 (timestamp-prefixed sortable). Most apps want v4 unless they have a specific reason otherwise.
Registering the commands
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![
extract_emails,
get_current_time,
format_date,
generate_uuid,
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
Standard invoke_handler plus generate_handler!. Each command is now callable from JS.
Using them from the frontend
import { invoke } from "@tauri-apps/api/core";
async function demo() {
const emails = await invoke<string[]>("extract_emails", {
text: "Contact alice@example.com or bob@test.org",
});
// ["alice@example.com", "bob@test.org"]
const time = await invoke<string>("get_current_time");
// "2026-05-07 14:23:18"
const formatted = await invoke<string>("format_date", {
formatStr: "%A, %B %d %Y",
});
// "Thursday, May 07 2026"
const id = await invoke<string>("generate_uuid");
// "550e8400-e29b-41d4-a716-446655440000"
}
Each call is just invoke plus the command name and args.
Other useful crates
A short list of crates that come up often in Tauri apps:
| Crate | Purpose |
|---|---|
| serde / serde_json | (de)serialisation. Already in every project. |
| reqwest | HTTP client. |
| sqlx / rusqlite | SQL databases. |
| tokio | Async runtime. Tauri brings it in by default. |
| anyhow / thiserror | Error types. |
| regex | Pattern matching. |
| chrono / time | Dates and times. |
| uuid | Unique identifiers. |
| image | Image decoding/encoding. |
| ring / sha2 | Cryptographic hashing. |
| base64 | Base64 encode/decode. |
| directories | OS-standard directories (config, data, cache). |
| notify | File system watcher. |
| walkdir | Recursive directory traversal. |
| rayon | Parallel iterators for CPU-heavy work. |
You will reach for one of these every other Tauri project.
Why this is a big deal
In a normal web stack, every "I want to do X" requires either a third-party API (with auth, latency, quota) or a backend service (with deployment, scaling, ops). In Tauri, you can:
- Manipulate images locally (no Cloudinary).
- Hash passwords without round-tripping to a server.
- Parse PDFs with the
pdf-extractcrate. - Stream a websocket with
tokio-tungstenite. - Run inference with
ort(ONNX) orcandle.
The user's machine becomes the backend. Latency is microseconds. No server to maintain.
Common mistakes
Forgetting feature flags. uuid without features = ["v4"] doesn't give you Uuid::new_v4. Read the crate's docs to know which features you need.
Using unwrap for parsing user input. A bad regex or a bad format string panics. Use ? and propagate errors.
Compiling regex per call. Use once_cell::sync::Lazy or lazy_static!.
Adding crates without checking maintenance. Some crates are abandoned. Check the last release date and the issue tracker before depending on it.
Adding huge crates for one function. chrono is fine for date work; time is a smaller alternative if you only need parsing. Profile your bundle size; a 50MB Tauri app is unusual but possible if you pull in big crates.
Series wrap
That's twenty-four lessons of Tauri. From a scaffolded "Hello, world" to a real app with database, file I/O, native dialogs, system tray, async commands, error handling, modules, and now Rust crate ecosystems.
The pattern across all of them: web frontend, Rust backend, IPC bridge with invoke and events, capabilities for security. Once you have those four ideas, the rest is library knowledge — what's available, what does what, how to use it.
The real next step is yours: pick a tool you wish existed on your desktop, build it. Tauri shrinks the bar from "a real desktop app" to "a weekend project."
Recap
cargo add to pull in any Rust crate. Wrap with #[tauri::command]. Register in generate_handler![]. Call from React with invoke. The whole crates.io ecosystem becomes your backend toolkit. Compile regexes once. Use Result for fallible operations. The directories, notify, walkdir, image, reqwest, sqlx, and friends are the workhorses of most Tauri apps.
Thanks for following the series.