Shopify App Store · Public app
SwiftSearch
A published Shopify app that replaces a store’s default search with an instant, typo-tolerant one — and then measures what that search is actually worth in revenue. Built solo, end to end: the search backend, the storefront widget, the merchant admin, billing, and the attribution model behind the numbers.
Search is the highest-intent surface in a store. Most themes waste it.
A shopper who searches has already decided what they want. The default experience makes them wait for a page reload, then punishes one mistyped character with an empty page. SwiftSearch answers as they type, absorbs the typo, and keeps the shopper inside the panel until they have added to cart.

Every decision in this app is a conversion decision wearing engineering clothes.
Typo tolerance is not a search feature, it is the difference between a result page and a bounce. Lazy-loading the UI bundle is not a build detail, it is refusing to tax the 90% of visitors who never search in order to serve the 10% who do. Attribution is not reporting, it is the only thing that tells a merchant whether any of the above mattered.
So the app is built around one loop: make the high-intent moment work, watch where it fails, give the merchant a one-click way to fix it, then prove the fix in revenue.
The moment before the first keystroke is not dead space.
Focusing the search box opens a merchandising surface: the shopper’s own recently viewed products, trending products, popular searches, and popular collections with live counts. Recently viewed is kept client-side and capped, so it works without asking the shopper for anything. A visitor who opens search without knowing the right word gets somewhere to go instead of a blank panel.

When the shopper already knows what they want, the result card is the product page.
Each result carries its variant picker and an Add to cart button, and the theme’s own cart drawer handles the rest — nothing about the store’s cart or checkout changes. On mobile the panel takes the full screen and the button confirms in place, turning into Added with a View cart link rather than throwing the shopper into a page transition mid-decision.

A zero-result search is a shopper telling you what you failed to sell them.
The admin lists every query that returned nothing, ranked by how often it happened, with Create synonym sitting on each row. That turns the bleakest number in search analytics into a one-click fix: map the term to products the store already carries, or take it as a stocking decision. The same view shows top queries with click-through and add-to-cart rates, so a popular query with weak CTR surfaces as a relevance problem rather than hiding inside an average.

Why the decay matters
- Clicks and add-to-carts from the last 30 days score every product.
- An add-to-cart is worth five clicks, because intent is not the same as curiosity.
- A score halves every seven days, so last week’s winner cannot coast on stale traffic.
- Scores are written back as partial document updates, so ranking moves between full catalogue syncs instead of waiting for one.
Relevance that learns from shopper behaviour, not from my opinion of the catalogue.
Text matching alone ranks a discontinued product the same as the one everybody buys. SwiftSearch folds a popularity score into the relevance sort, built from what shoppers actually did in the panel: clicked, or added to cart.
The half-life is the part that matters. Without decay, a product that had one good week in January outranks the store’s current bestseller forever, and the merchant slowly stops trusting the results. A ranking signal that cannot forget is a ranking signal that goes stale in public.
I would rather under-report than take credit I cannot prove.
Line item, not order.
An order that mixes a searched-for product with a browsed one credits only the searched-for line. Whole-order attribution would have been easier and would have flattered the number.
Last touch, seven days.
Long enough to cover the normal consider-then-buy gap, short enough that a search from last month cannot claim today’s order.
No customer data.
A web pixel on checkout_completed sends order id, currency, totals, and line items. The app never requests the read_orders scope and stores no personally identifiable data.
Consent gated, so it under-reports.
Shoppers who decline analytics consent or block storage are invisible to the pixel. A sale is only ever counted when a real engagement preceded it, which makes the figure a floor rather than an estimate.
Share of tracked sales, never of the shop.
The dashboard says share of orders this pixel saw. It never implies a share of the store’s true totals — those live in Shopify Analytics, and pretending otherwise is how attribution dashboards start lying.
One currency, no invented FX.
Money is held in integer minor units of the order’s presentment currency, so a multi-currency shop aggregates per currency instead of being silently converted at a rate nobody chose.
Speed is a conversion feature, so the widget is priced like one.
The storefront pays 4.5 KB gzipped up front. That core script binds to whatever search input the theme already has, watches for the first focus or hover on it, and does nothing else. Only then does it fetch the 18 KB UI bundle — early enough that it is ready before the shopper finishes a keystroke, and never at all for the visitors who never search.
It installs as a theme app embed, so there is no theme code to edit and nothing left behind on uninstall. It does not take over the cart, and it does not change how the store’s products are indexed by search engines. An app that costs a merchant Largest Contentful Paint in exchange for better search has not made them any money.
What shipping it publicly demanded
- Shopify App Store review: listing, screenshots, screencast, and testing instructions.
- Billing through the Shopify Billing API across four catalogue-size plans.
- GDPR compliance webhooks and a hosted privacy policy.
- Webhook-driven catalogue sync so the index tracks products, collections, and bulk operations.
- A widget that inherits the host theme’s type, colour, and card shape instead of fighting it.
The stack, named honestly.
- React Router 7
- TypeScript
- Typesense
- Prisma
- PostgreSQL
- Shopify App Bridge
- Polaris web components
- Theme app embed
- App proxy
- Web Pixels API
- Shopify Billing API
- Docker
What this case study should prove.
I can take a conversion problem the whole way down: identify the highest-intent surface in a store, rebuild it, instrument it so the merchant can see where it leaks, and then hold myself to an attribution model strict enough to tell me when I was wrong.
SwiftSearch is not a demo. It is a public Shopify app with billing, compliance webhooks, and merchants who can uninstall it the moment it stops earning its place on the page.
If you remember one thing
- Instant, typo-tolerant search with add-to-cart inside the results.
- Zero-result queries turned into a one-click synonym fix.
- Ranking that learns from clicks and add-to-carts, and forgets on a seven-day half-life.
- Line-item, consent-gated revenue attribution that deliberately under-reports.