Localsalad.is



Overview
Localsalad.is is the customer-facing ordering and marketing website for Localsalad, an Icelandic salad restaurant chain. The site lets visitors browse the menu, customise a salad, check the nutritional breakdown of every item, order for pickup or courier delivery, place company orders, subscribe to the newsletter, and find branches on a map — all in Icelandic or English.
The site follows the standard VISKA customer pattern: Prismic manages the marketing content and menu, while the product catalog, pricing, cart, and checkout come from the shared Upsell platform via @upsell/kit and the public-api.upsell.is/v2 API. There is no local database — Prismic is the content source and SWR (via the kit) handles client-side data fetching.
Tech Stack & Rationale
Layer | Technology | Why |
|---|---|---|
Framework | Next.js 14 (App Router), React 18 | Server-first rendering with locale-based route groups ((main), (clean), (plain), (prismic)) |
Language | TypeScript | Strict typing across slices, kit types, and API responses |
Styling | Tailwind CSS 3.4 | Rapid, consistent brand styling |
CMS | Prismic + Slice Machine | Editors manage pages, layout, and slices without code changes |
Client data fetching | SWR 2.2 (via @upsell/kit) | Revalidation and caching for cart, checkout, and client state |
Business logic | @upsell/kit + public-api.upsell.is/v2 (Axios) | Shared cart, checkout, and Apple Pay logic with a MobileApp-authenticated store ID |
i18n | LinguiJS (is, en) + Crowdin | Compile-time message catalogs, Icelandic-first locale redirect |
Maps | Mapbox GL + react-map-gl | Branch locator |
Forms & email | SendGrid + newsletter route | Company/contact forms and the newsletter endpoint |
Animation | GSAP, Swiper, Framer Motion, Lottie | Hero motion and carousels |
Monitoring | Sentry | Error tracking in production |
Hosting | Vercel | Preview + production deployments via Nx |
Architecture
A request flows from the locale middleware through server-rendered pages, then hands off to SWR-backed client components that talk to the Upsell API. Prismic content is fetched server-side with tag-based caching and preview support.
The flow runs: Browser request (/is/menu) → middleware.ts (locale redirect → is) → Server Components (RSC + generateMetadata) → SWR hooks (client revalidation) → public-api (upsell.is/v2) → Vercel (edge / CDN); Browser request (/is/menu) → middleware.ts (locale redirect → is) → Prismic client (tag cache + preview) → @upsell/kit (cart · checkout).
- Locale routing — middleware.ts reads the request pathname and, when no locale prefix is present, redirects to /is/... (Icelandic default). API and well-known paths are exempt.
- Server rendering — (main) pages are React Server Components that fetch Prismic pages documents (with their linked layouts document) through a tag-cached client, then render the slice zone.
- Client interactivity — interactive features (cart, salad customizer, nutrition calculator, deals) hydrate into client components that read the product catalog through @upsell/kit, with SWR managing revalidation and optimistic cart state.
- Checkout — the (clean) group renders checkout steps (shipping, summary, verification) and the (plain) group renders the success page; payment goes through the Upsell API with Apple Pay via useApplePay and OTP verification via react18-input-otp.
- Newsletter — a dedicated /api/newsletter route persists signups, while company/contact form slices submit through the platform.
Folder Structure
customers/localsalad-web/
├── app/[locale]/
│ ├── (main)/
│ │ ├── (customization)/ # products/[productID], deals/[dealID]
│ │ ├── store/ · login/ · profile/
│ ├── (clean)/checkout/ # shipping · summary · verification
│ ├── (plain)/checkout/success/
│ ├── (prismic)/[[...slug]]/ # CMS-driven pages + slice-simulator
│ └── layout.tsx
├── components/ # auth, cart, checkout, deal, input, menu, product, profile, store…
├── contexts/ # TopBannerContext, layout
├── slices/ # 16 slices (Hero, Menu, Branches, NutritionalInformation, …)
├── customtypes/ # pages, layouts, top_banner
├── constants/ # branches, customization, order, products
├── lib/ # api.ts, generateMetaData.ts, sortBranches.ts, hooks/
├── locales/translations/ # en.po, is.po (Crowdin)
├── middleware.ts · appRouterI18n.ts · prismicio.ts
└── next.config.js · tailwind.config.tsContent Model
Prismic is the single content source. Three custom types drive the site:
Custom type | Kind | Purpose |
|---|---|---|
pages | Repeatable | Marketing pages — uid, page_title, a layout link, a slice zone, and SEO metadata (meta_title, meta_description, meta_image, meta_keywords) |
layouts | Repeatable | Site chrome composed of navbar, children, and footer slices; linked from each pages document |
top_banner | Singleton | An optional promo bar (paragraph, cta_text, cta_url, active) surfaced by the TopBannerContext |
The pages slice zone exposes 13 slices: hero, menu, special_product, card_grid, branches, text_image, two_cols_content, banner, nutritional_information, paragraph, and the form slices contact_form and company_form.
Impact & Results
- In production for the Localsalad salad chain in Iceland, with nutrition details, company orders, and newsletters.
- One of 16+ customer sites served by the shared VISKA monorepo platform.
My Contributions
Worked on the Localsalad site as part of the VISKA monorepo team:
- Added the shopping-bag flow and improved the menu (deals view on the menu, the deals page, and menu navigation).
- Built the nutrition experience: the nutritional page, the nutrition modal, nutrition quantity handling, and the nutrition scroll fix.
- Added the newsletter and email form, the SMS-info notice, and the reply-to field on the contact form.
- Improved the hero and branch map, added products to categories, and ordered branches with the sortBranches utility.
- Set up Crowdin for translations, completed the Icelandic catalog, and fixed the included-items and success-page issues.
References
[1] Localsalad, "Localsalad.is — Salatbar," localsalad.is, 2025. [Online]. Available: https://localsalad.is
[2] Vercel, "Next.js Documentation," nextjs.org. [Online]. Available: https://nextjs.org/docs
[3] Prismic, "Prismic Documentation," prismic.io. [Online]. Available: https://prismic.io/docs
[4] Vercel, "SWR — React Hooks for Data Fetching," swr.vercel.app. [Online]. Available: https://swr.vercel.app
[5] LinguiJS, "LinguiJS — Internationalization," lingui.dev. [Online]. Available: https://lingui.dev
Comments
Leave a Comment
You must be signed in to comment
0 Comments
No comments yet. Be the first to comment!