Introduction

IIIF (International Image Interoperability Framework) is an international standard for image delivery widely used in digital archives and museum collections. The IIIF Content Search API allows you to search annotations (notes and tags) within manifests.

However, the IIIF Content Search API typically assumes server-side implementation, and it has been considered difficult to implement on static sites (GitHub Pages, Vercel, Netlify, etc.).

In this article, I will introduce a method for implementing the IIIF Content Search API on the client side using Service Workers. This approach enables the use of search functionality in IIIF viewers such as Mirador even on static sites.

Challenge

How Traditional IIIF Search API Works

[Mirador]GET/search?q=keyword[Server]SearchprocessingJSONresponse

The IIIF Content Search API requires an endpoint that receives query parameters (?q=search term) and returns search results in JSON. This assumes dynamic server processing.

Limitations of Static Sites

On static sites:

  • Dynamic responses based on query parameters cannot be returned
  • Server-side search processing cannot be executed
  • Only static JSON files can be served

Solution: Request Interception with Service Workers

A Service Worker functions as a proxy positioned between the browser and the network. By leveraging this, we can intercept search requests and perform search processing on the client side.

Architecture

[[[MSMieirrraadidoGcorEer]TFER]Wexe/otesirccpikhuoietnDfrsedi/]tssasipitenltiaaecrIy/IcIsnihIsetnFeaediarrenCrccxoche.Jnh/pjatitsvernoanednStsecux(rSl.fietjipassrtroscnth?qtA=iPkmIeeyfwooonrrlmdya)t

Implementation

1. Generating the Search Index (At Build Time)

First, generate a search index from annotation data.

f}unca}r}bcon)e;utnne};t'tteiison)u@yonlottttlmacmt;rcpttdnareaonaaanoear-etixntnnnr?:n:lisgnietgiovig{tEeeetos:uvtafe`ce'nsanrn.aaaset$axStreispagttIs:{ntercre.uneiidtcv'aihasfsn:oo:Iaaa:re-tohonndnnscsie=r(.a:I`:nvI'h:nSE{bnd$oadhIde[aona:{`.stneea]cdono$tItdnxr;hy.n`p{adpet.c(_bo$tor}:xrjhavo.{ipg#/'isInadmootex,ennlyopnityisdou_ttso_wi.eelii.nrhilx=,avobse=fe(>nana.g$.nagtssbi{ign{ui.eaoaotnaobUsnn/hognarena,te,slUopa,e}r.itU/lt/irc}asola/ren}nmgas/vaer,aantcnsi_honfr/p$ee2tt{sg/iaaticotn.ooninjnnsoos}t)n.o`ecnx{$a`t{n,.avjnasnsoo_n.i'in,dd}e`x,}`,

Generated index.json:

{}""""]@ttecyon{}opttnear"""""""t"litlmacmte:Eeeaonaaaxnsxntnnnrt"t"tgiovig"Sr:"uvtafe:ei:aaasetae[gttIs""rs"eiidt:hc""oo"Ith::nn:d"tI"I"hpn1":d":t:d2j"ht/e,1a":t"p/x6"cth:i",o"pt/i,mh:t/imt/pefet/:x.npe/ait:x/moi/aep/n/mxlagepaep"xlm.i,aepc/m.lospcemelo./aemccr"./oac,ccmnhoa/v/mnma2/vas/aan/cnsi1onf"n1ett"sea,txt.tij.osjnos/no1"n",",,

2. Service Worker Implementation

Create a Service Worker that intercepts search requests and processes them on the client side.

ca}f}f}s}a}ssosuue)seenyi}ccsrncrnr}lci};ycccccr}lliSsLnfooeeEcoeGce;Hfofnoooooe)ffietocrnnatxtnt..}ett'''w}r}h}a.necnnnnnth;..iaa(essrueisufm)niu@@@i,e)i)nasI(vsssssueaafrsdfstttcrcotriaemba)eorcitt'ts''mr}o)t'amba)ddtnueftttttradd-ceueuhnunnlpnaef;rnnodyh@oo@@oe,n,s@naefldtrnundddshasnarriItnt(ttftan'pittuitts'c::tntfteEueltnqiirreEEerecrnennieeoieercGoetf{t:enyardyio@hyocoevrr..cunneenrvvaicatcsddnxrnrnyherreoe':plc'pvutarrpthrrfelcprteddsseseernhrihspeedsemd(t,:te:rxr:e:e:earyr.eea:e:eneaeireeupw:nncdIcoIeoxxeecaeer:rmte{'s'tcpses't:tt=ptsoyxxlotthenhnnanCxaulxnyqsea'q':r:r:iee:nu:irrcLthpnUtnR{LL-xddrs=a;rti.tuust:use.o:'tlo.r.hinno=r=sseiiseilececcezer=erpSec'sren:rrt'nm.aseIanhles'sswcxnoxhahhSeny>rroe's:sueno:{.ysssabfetwImdaua=DpCtt.aCdaCI=weedtyonahtAclsta'e..e:tetveIeWnr=waooeejcaedana.aQr=(,u'srtUn:tur:rcntmacfeenUF.idlaetnnnnshcxScdaisrui>{n,ectrnLslyA.ntaar[horneRitl.`ixasteeehehewteceedhploa.t.netrrpcr,rtrLsnhes$teeerreaexathrseiiR:,tylsann:yg(h.es((ec(Se{c=(n((r.Cir((ynnne/aee.notC.er:e,'ealheauluJt''=chateiitgs/trnmntrottHnfvruaarrotfS-iahacsnn=rIpii'gaoayne=iteecdnrclaeoOTncnIshfpddycIoio,tptt.tx>trtnhedch.dSrNysten(eeoeeq.oIninh(aimet'yctslhPoSem.ptiwdi.tnxxutnFsfLrtoon(,.h.r(eRareaaseavengcsU,eete.iintt{a're'Seriartt'laMxdeherrxeC(is=o'iAn,eq/eqagrcSr:lta(et(.lqytxorot>n,vsnqusaumichei'epix(ij,u..tne/'IaTo(ueeresnh(an','(nUinsetttqa,(dteteesacs.}Iirga,)drndoirooeup{,ixavstrhtg$nncip;eldennyLLneiottetscR(e{ddhfp(x)ex(d)oots/n'in.hertueeRyleU)xU)ewwts,,otu/qe(rxxe(i=vrUr;x{eeSUen)riuq'l(,src>el{rl)rreraIlneuq.ipean)l);CCalrd=)dse'pnqostst);aar,c]>;ets)adunpie){;ssch,x(tteesooleehr/{.e,hxrennf=((e1jvnUy(s/.>))As/seuar)rejs..Puconrml;eDsketiIlontl'e)qaoivrntn'.);};utnpeicfst)r`ea'Wnmlo)ee{;s)at(urx&qt,}i.)dm{t&u.tw;ea.eu{iastjusrni(srtlgtnol,,(Uon.)nr'sur)tm,ere;iaalsllr)u(ic)lczh;tlePsida)eQr;nuatemsrs.y.c)hl)aasi(m'(q)'))));{

3. Service Worker Registration

Register the Service Worker when the application starts.

e}xpi}t}}rofrercryccrccrgt(oeooeaoei!nt{nnttntsa(sussucsuts'ortorhoreyslnlnlnrneere(e-cr.fe.te.fivwaglrreaifialiourrliucrssgeorsfnenet(;roe-cW(;r')r;sto'aI(eirStI{'aokeiIRrneroFecrvnghr'iSi-ec=essgieatwinarr.sWwcattnoahtseariirvktSoIieenIgrnrIaavfFtiviaSosiciergela)naer)otWdctoo:h{rr'Ss.k,euserperevprriovrrcrieoetcgrWeei)odWs;r'otk)ree;krree(rd).':r)e;Pgriosmtiesre(<'b/oioiliefa-ns>ea{rch-sw.js');

4. Search Service Declaration in the Manifest

Declare the search service in the IIIF manifest.

{}""""]@itscdye{}o"prn:ev"""""t"i@@@ple":ccitraxheodyobtt""n"pfe"tM:t:eil:pae"l":n[x":e:"/ith"h/f"t":"tee:tSStxspe"epat":aha:m"h/rtr/p,t/ctc/ltehphiepxS:i.:ae/wic/mr/ifo/pit.miliihiieciimi.efn/afc1.an.o"itpiim,ohifo/i/esaspsaeprtpaime.ir/asj/csnessheinoe/aftnairea"rncst,cdhtihe/"o/x1n1.//js3cse/ooacnnrot"cne,htx"et,x.tj.sjosno"n,",

5. Usage with Mirador

Simply registering the Service Worker before initializing Mirador enables the standard search functionality.

ie}mxppu}Moos,irrerrttEe[afg]d{dfRi)oeees;rrfcgtMVeatieiigu(srreil(tIawst)eIdetrIoref=Fr.ru>SStIneeisIc{ranxItvriFiictSochieneSaaelrMWriciovzhrriaSakctedeeirorWovroniVrcikeeeWwroe(rr)k(;e{rm}anfirfoemst'U@r/lli}b)/r{egister-iiif-search-sw';

Operation Flow

1234567891.........0.USMUMGSSSRseisiEeteeMerrerTraasirvaravtrpriddiicoaocoticchndperyriesoepiiiernWlesfWnssoose/odidransreesitkdidikxxshesnste.egpererjceltta/sunapihhsioteyaseesennersgeatdaermsareitteaercrsiehgnachcndeiirh/efsfcipeJistehrnttaneesedscvartbqehaIreouxteSIcdaxe.hdcIhnsjerFdts(irorcpCedneatose?qcnutquhtle=eeetcmsdnstat)tspStehaercsheaArPcIhfsoerrmvaitce

Benefits and Limitations

Benefits

ItemDescription
Static site compatibleWorks on GitHub Pages, Vercel, Netlify, etc.
No server requiredEverything is completed on the client side
Low costNo server maintenance costs
FastIndex is cached in the browser
No Mirador modification neededStandard Mirador works as-is

Limitations

ItemDescription
Index sizeInitial loading may take time for large collections
HTTPS requiredService Workers only work in HTTPS environments (localhost is an exception)
Browser supportNot supported in IE11 (all modern browsers are supported)
Full-text search limitationsCannot achieve the same precision as advanced full-text search engines

Handling Large Collections

When the index size becomes large, the following measures are effective:

  1. Index splitting: Split the index per manifest
  2. Lazy loading: Fetch the index only when searching
  3. Compression: Reduce file size with gzip compression
conIsntdeixndUeRxLUrpler=m`a/niiifiefs/t${siteId}/3/${itemId}/search-index.json`;

Summary

By leveraging Service Workers, the IIIF Content Search API can be implemented even on static sites. The key points of this approach are:

  1. Generate search index at build time
  2. Intercept requests with Service Worker
  3. Execute search on the client side
  4. Return responses in IIIF standard format

This enables providing search functionality on static sites without modifying IIIF viewers like Mirador. Please consider this as an option for providing a rich search experience while keeping costs low with a serverless approach when building digital archives.