No Server, No Backend, Just Blazor WebAssembly Doing Semantic Search
A developer shows how to implement semantic search in a Blazor WebAssembly app hosted on GitHub Pages. By generating vector embeddings at build time and computing query vectors in the browser, the site can find documents by meaning without any backend. The process uses MiniLM ONNX models, JavaScrip…
Semantic search lets users find information based on meaning rather than exact keyword matches. In a typical web application, this requires a server‑side service that can compute vector embeddings and perform nearest‑neighbor queries. However, a recent project demonstrates that a fully client‑side Blazor WebAssembly app can deliver the same functionality while being hosted on a static file server like GitHub Pages.
What Is Vector Search?
Vector search transforms data—text, images, or audio—into a numeric array called a vector. Each element of the vector represents a feature of the data, and the overall vector captures its semantic content. Once data is encoded as vectors, similarity can be measured by computing a distance metric. The smaller the distance, the closer the meanings. In practice, cosine similarity is used instead of Euclidean distance because it normalises vectors and produces a score between –1 and 1, where values near 1 indicate high similarity.
Unlike keyword search, which looks for exact string matches, vector search can retrieve documents that are conceptually related. For example, a search for "apple" can return pages about "fruit" or "banana" because their vectors are close in the embedding space.
Building the Search Index at Build Time
The Blazing Story documentation site is built with Markdown files. During the CI pipeline on GitHub Actions, a C# console application runs on an Ubuntu runner to process each Markdown file. It loads a quantised MiniLM ONNX model (all‑MiniLM‑L6‑v2) and a tokenizer vocabulary from Hugging Face. The console app tokenises each document, performs mean pooling over the per‑token vectors, and normalises the result. The final 384‑dimensional vector for each document is written to an index file that is bundled with the static site.
Because the index is generated offline, the client never needs to download the heavy model. Only the pre‑computed vectors and the small index file (tens of kilobytes) are shipped to the browser.
Computing Query Vectors in the Browser
When a user types a search query, the Blazor WebAssembly app must convert that query into a vector using the same MiniLM model. Since Microsoft’s ONNX Runtime does not run in WebAssembly, the project uses the JavaScript library @xenova/transformers. The library fetches the quantised ONNX model and tokenizer files from Hugging Face, caches them in the browser, and exposes a pipeline function that returns a vector for any input text.
The Blazor app calls this JavaScript function via interop. The resulting vector is normalised, and cosine similarity is calculated against every vector in the pre‑built index. The documents with the highest similarity scores are displayed as search results, giving users a semantic search experience entirely on the client side.
Why This Matters for Static Sites
Static hosting platforms like GitHub Pages are free, fast, and simple to maintain. By moving the search logic to the browser, developers can add powerful search capabilities without paying for server infrastructure or managing APIs. This approach also reduces latency, as all computations happen locally, and it improves privacy, since user queries never leave the client device.
While the example focuses on English documentation, the same pattern can be adapted for other languages by selecting appropriate language‑specific models and tokenisers. The key requirement is that the same model is used for both index generation and query embedding.
Getting Started
To replicate the setup, clone the Blazing Story repository and examine the two GitHub Actions workflows: one for building the site and generating the index, and one for deploying to GitHub Pages. The console app lives in the ConsoleAppEmbeddingDemo folder, and the Blazor WebAssembly demo is in BlazorWasmEmbeddingDemo. Both projects reference the same quantised ONNX file from the Xenova/all-MiniLM-L6-v2 model on Hugging Face.
Once you have the code, run the console app locally to generate an index for your own documentation. Then build the Blazor WebAssembly project, serve it locally, and test the search box. You’ll see that the first ten elements of the query vector match the console app’s output, confirming that the client and server‑side embeddings are consistent.
For production, consider chunking long documents, optimizing the index format, and adding pagination or filtering to improve usability. Nonetheless, the core idea—pre‑compute embeddings, ship them with the site, and compute query embeddings in the browser—remains the same.
In summary, semantic search can be achieved on a purely static site by combining offline vector generation with client‑side inference. This technique opens up advanced search features for a wide range of web projects without the overhead of backend services.
Next Steps
Explore other open‑source models that support different languages or modalities, experiment with alternative similarity metrics, and integrate the search into larger Blazor applications. The Blazing Story documentation site already demonstrates the feasibility; you can extend it to your own projects and share your experiences with the community.
Why it matters
Implementing semantic search on a static Blazor WebAssembly site eliminates the need for backend services, reducing hosting costs and improving privacy while still providing advanced search capabilities.
Key points
- Vector embeddings enable meaning‑based search without keyword matching.
- Build‑time C# console app generates document vectors and an index file.
- Client‑side Blazor WebAssembly uses @xenova/transformers to embed queries.
- Cosine similarity between query and document vectors drives ranking.
- The approach works entirely on static hosting like GitHub Pages.
- Extending to other languages requires matching models and tokenisers.
Frequently asked questions
Can I use this technique with other Blazor frameworks?
Yes, any Blazor WebAssembly project can integrate the JavaScript @xenova/transformers library via interop to compute embeddings.
Do I need a powerful computer to run the console app?
The console app runs on a typical Linux machine; the quantised ONNX model is only about 22 MB, so it’s lightweight.
Will the search work offline?
Once the site and index are loaded, the browser can perform searches without an internet connection, as all required resources are cached.





