AV
HomeAboutProjectBlog

© 2026 Ave syah Shina. All rights reserved.

  1. Home
  2. Projects
  3. Uos.is

Uos.is

August 1, 20255 min read
View Project
Next JsTypescriptTailwind CSSSWRPrismic
1 / 3

Overview

Uos.is is the public website for Orkustofnun, the National Energy Authority of Iceland. It is a bilingual (Icelandic default, English) content site that publishes news, events, public disclosures, regulations, and an open data catalogue of boreholes, maps, drawings, and licenses. The site is delivered from Prismic as a headless CMS and rendered by a Next.js App Router frontend.

The project is heavily integrated with third-party services. Algolia powers full-text search across content documents, SendGrid drives two email systems (a newsletter and a public-disclosure email-alert subscription flow with double opt-in confirmation), and Mapbox/MapLibre renders the map-based data pages. Borehole detail pages embed Plotly charts generated from measurement data.

Internationalization is a first-class concern. The site defaults to Icelandic routes (/frettir, /vidburdir, /opinber-birting) with parallel English routes (/en/news, /en/event, /en/public-disclosure), and a custom middleware layer resolves CMS-defined redirects for both locales.

Tech Stack & Rationale

Layer

Technology

Why

Framework

Next.js 15 (App Router), React 19

Server Components, per-locale route groups, and file-based API routes in one framework

Language

TypeScript (strict)

Typed Prismic document models (prismicio-types.d.ts) and API payloads

Styling

Tailwind CSS 3

Utility-first styling with a theme-variable system for per-layout color schemes

CMS

Prismic + Slice Machine

Headless content with 14 custom types and ~39 reusable slices, auto previews and cache-tag revalidation

Search

Algolia (v5)

Instant full-text search with date-sorted results and type/date-enriched suggestions

Email

SendGrid (@sendgrid/mail)

Newsletter and email-alert subscriptions with confirm/unsubscribe API flows

Data viz / maps

Mapbox GL + react-map-gl, Plotly.js

Interactive maps for data pages and chart rendering for borehole measurements

Client data

SWR 2

Revalidation for search and feature data on the client

UI helpers

Headless UI, Embla Carousel, react-select

Accessible modals/menus, carousels, and searchable selects

SEO

schema-dts

Typed JSON-LD structured data generation

Architecture

A request is first inspected by middleware.ts, which resolves CMS-defined redirects, then routed into a per-locale route group where a server component fetches Prismic content and renders the matching slice. Mutations and integrations happen through dedicated API routes.

The flow runs: HTTP request → middleware.ts (redirects · locale) → [locale] route group (is · en) → Server component (Prismic fetch); HTTP request → middleware.ts (redirects · locale) → [locale] route group (is · en) → SliceZone render (39 slices); app/api routes (email · search · feeds).

  1. Middleware / redirects — middleware.ts reads a custom_redirects document from Prismic (cached for 5 minutes), extracts the locale from the path (is or en), and issues locale-aware 301/302 redirects. Content slugs are mapped to localized routes (e.g. /frettir/:uid → /en/news/:uid).
  2. Routing — the app uses the (routes)/[locale]/(prismic) route groups, with (is), (en), and (default) sub-groups. The default group holds the data catalogue pages (boreholes, drawings, licenses, maps), while (is) and (en) hold the localized news, event, and public-disclosure routes, plus a dedicated search route.
  3. Content fetch — server components call createClient() from src/config/prismic.ts, which registers a route resolver (news, public_disclosure, event per locale) and caches Prismic requests with the prismic/redirects tags in production. On content publish, the /api/revalidate route purges those tags.
  4. Slice rendering — page content is composed through a SliceZone of the ~39 slices (Hero, News, PublicDisclosure, StatisticOverview, AirQuality, Borehole charts, etc.), with a layoutUid context that themes slices per layout (e.g. the Roe theme).
  5. Integrations — dedicated API routes talk to third parties: /api/algolia/sync-pages pushes content to Algolia, /api/email-alert/* handles SendGrid subscriptions, and /api/integrations/icelandic-weather and /api/integrations/legislations proxy the Vedur weather feed and Icelandic legislation data.

Folder Structure

uos/
├── app/ # Next.js App Router (route groups + API routes)
│ ├── (routes)/[locale]/ # localized pages: (is), (en), (default) data catalogue
│ ├── (email-alert)/ # email-alert success/error confirmation pages
│ └── api/ # algolia, email-alert, events, feedback, integrations, preview, revalidate
├── customtypes/ # Prismic models (page, news, event, layout, employee, menus, …)
├── slices/ # ~39 reusable Prismic slices
├── src/
│ ├── components/ # feature + UI components (news, events, boreholes, search, email-alerts)
│ ├── config/prismic.ts # Prismic client + route resolver
│ ├── data/ # local JSON (email subscriptions) + logs
│ ├── features/ # boreholes, events feature modules
│ └── shared/ # constants, i18n, hooks, lib, types, utils
├── middleware.ts # locale-aware redirect resolution from Prismic
├── next.config.ts # rewrites, image remote patterns, serverActions size
├── types/ # ambient declarations (plotly, react-map-gl)
└── scripts/ # UID-check and redirect-test helpers
  • app/ — the App Router tree; route groups separate localized content from the data catalogue and API routes.
  • customtypes/ — the Prismic content model: page, news, event, public_disclosure, layout, employee, category, content_data, custom_search_data, custom_redirects, and the five menu types.
  • slices/ — the reusable slice library consumed by Slice Machine and the SliceZone.
  • src/ — shared source: the Prismic client config, feature components, i18n, hooks, and utilities.
  • middleware.ts — the redirect engine described above.

Content Model

Uos.is has no relational database. All editorial content lives in Prismic, while a few runtime files are stored as local JSON in src/data/.

Custom type

Purpose

page

Generic slice-composed page, resolved at /:uid

news

News articles, localized (/frettir/:uid, /en/news/:uid)

event

Events (/vidburdir/:uid, /en/event/:uid)

public_disclosure

Public disclosure documents (/opinber-birting/:uid, /en/public-disclosure/:uid)

layout

Site layout with Navbar/Footer/Children slices

employee

Staff directory entries

category

Taxonomies for news/events/public-disclosure

content_data

Free-form slice content bundles

custom_search_data

Hand-curated search suggestions (title/keyword/link/description)

custom_redirects

CMS-managed redirects read by middleware.ts

*_menu (×5)

Administration, Electricity, Energy, Environment, HR mega-menu definitions

Subscriber state is kept outside Prismic: src/data/subscriptions.json (and backups) hold email-alert subscriptions, while src/data/email-subscriptions.json tracks the newsletter list.

Impact & Results

  • In production as the public website of Orkustofnun, the National Energy Authority of Iceland — publishing news, disclosures, regulations, and an open data catalogue.
  • Serves a national institution with bilingual content, full-text search (Algolia), and map-based data pages.

My Contributions

I was the lead developer on this project (627 commits). Key work:

  • Built the bilingual routing layer — the (routes)/[locale] route groups, the is/en route resolver, and the middleware.ts locale-aware redirect engine that reads custom_redirects from Prismic and maps slugs between Icelandic and English routes.
  • Implemented the Algolia search experience: the /api/algolia/sync-pages sync route, date-sorted search results, and search suggestions enriched with type and date info.
  • Added the per-layout theming system (layoutUid context) so slices like News/PublicDisclosure can restyle themselves for alternate layouts such as the Roe theme, using theme color variables.
  • Built and refined slice UI: card-grid button variations, RichText tooltip/link/id-attribute support, and search loading skeletons, plus fixes for hydration mismatches in Hero V2 and the search page.
  • Wired the email-alert flow (subscribe/confirm/unsubscribe/status API routes) against SendGrid lists.

References

[1] Orkustofnun, "National Energy Authority of Iceland," [Online]. Available: https://orkustofnun.is

[2] Next.js, "Next.js 15 Documentation," nextjs.org, 2025. [Online]. Available: https://nextjs.org/docs

[3] Prismic, "Prismic documentation — headless CMS," prismic.io, 2025. [Online]. Available: https://prismic.io/docs

[4] Algolia, "Algolia Search API documentation," algolia.com, 2025. [Online]. Available: https://www.algolia.com/doc/

[5] SendGrid (Twilio), "SendGrid Email API," twilio.com, 2025. [Online]. Available: https://sendgrid.com

[6] Mapbox, "Mapbox GL JS documentation," mapbox.com, 2025. [Online]. Available: https://docs.mapbox.com/mapbox-gl-js/

Comments

Leave a Comment

You must be signed in to comment

0 Comments

No comments yet. Be the first to comment!