[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"blog-article-the-tech-stack-behind-web-highlights-a-chrome-extension-with-over-10-000-weekly-users":3},{"meta":4,"html":33,"authorName":34,"authorFacebookUrl":35,"authorProfileImage":36,"authorWebsite":37,"authorJobTitle":38,"authorTwitterUrl":39,"wordCount":40,"recommended":41},{"id":5,"slug":6,"type":7,"title":8,"excerpt":9,"metaTitle":10,"metaDescription":10,"canonicalUrl":10,"featureImage":11,"publishedAt":17,"createdAt":18,"updatedAt":19,"featured":20,"readingTime":21,"tags":22,"author":32,"recommendedSlugs":10},"63f9fe75b30b15062f3489f2","the-tech-stack-behind-web-highlights-a-chrome-extension-with-over-10-000-weekly-users","post","Web Highlights Tech Stack: Chrome Extension With 100k+ Users","Explore the tech stack of Web Highlights, a Chrome extension using Web Components and Vue.js for saving and highlighting web content easily.",null,{"url":12,"alt":13,"caption":14,"width":15,"height":16},"\u002Fblog\u002Fcontent\u002Fimages\u002F2023\u002F02\u002FYellow-Bold-Education-Course-Vlog-YouTube-Thumbnail.png","Tech Stack Behind Web Highlights - PDF & Web Highlighter","Tech Stack Behind Web Highlights — PDF &amp; Web Highlighter",1280,720,"2023-02-27T06:55:25.000+00:00","2023-02-25T12:26:29.000+00:00","2024-09-19T19:40:18.000+00:00",false,10,[23,26,29],{"name":24,"slug":25},"Web Development","web-development",{"name":27,"slug":28},"Web Highlights","web-highlights",{"name":30,"slug":31},"My Journey","my-journey","marius-bongarts","\u003Cp>\n  Web Highlights is a\n  \u003Ca href=\"https:\u002F\u002Fchromewebstore.google.com\u002Fdetail\u002Fweb-highlights-pdf-web-hi\u002Fhldjnlbobkdkghfidgoecgmklcemanhm\">\n    Chrome\n  \u003C\u002Fa>\n  and\n  \u003Ca href=\"https:\u002F\u002Fmicrosoftedge.microsoft.com\u002Faddons\u002Fdetail\u002Fweb-highlights-pdf-we\u002Fghdpaajjhmlfkogblgeibaabcpjfgldn\">\n    Edge\n  \u003C\u002Fa>\n  extension created to help users highlight and save important web content. With over 100,000 weekly users, it has\n  become a go-to tool for many who need to keep track of key information online.\n\u003C\u002Fp>\n\u003Cfigure class=\"kg-card kg-bookmark-card\">\n  \u003Ca class=\"kg-bookmark-container\" href=\"https:\u002F\u002Fweb-highlights.com\u002F\">\n    \u003Cdiv class=\"kg-bookmark-content\">\n      \u003Cdiv class=\"kg-bookmark-title\">Web Highlights - PDF &amp; Web Highlighter\u003C\u002Fdiv>\n      \u003Cdiv class=\"kg-bookmark-description\">\n        Best highlighter for the web. Just like you do on books, highlight on any web page or PDF, and take notes.\n        Organize with tags and find your research in the app.\n      \u003C\u002Fdiv>\n      \u003Cdiv class=\"kg-bookmark-metadata\">\n        \u003Cimg class=\"kg-bookmark-icon\" src=\"https:\u002F\u002Fweb-highlights.com\u002Ffavicon.ico\" alt=\"\" \u002F>\n        \u003Cspan class=\"kg-bookmark-author\">Web Highlights - PDF &amp; Web Highlighter\u003C\u002Fspan>\n        \u003Cspan class=\"kg-bookmark-publisher\">Marius Bongarts\u003C\u002Fspan>\n      \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n    \u003Cdiv class=\"kg-bookmark-thumbnail\">\u003Cimg src=\"https:\u002F\u002Fweb-highlights.com\u002Fimages\u002Fwh-thumbnail.png\" alt=\"\" \u002F>\u003C\u002Fdiv>\n  \u003C\u002Fa>\n\u003C\u002Ffigure>\n\u003Cp>\n  In this article, I’m excited to share with you the technology stack that powers the extension and brought this project\n  to life.\n\u003C\u002Fp>\n\u003Ch3 id=\"introduction\">Introduction\u003C\u002Fh3>\n\u003Cp>When I started developing Web Highlights 5 years ago, I wasn’t too worried about the tech stack.\u003C\u002Fp>\n\u003Cp>\n  It was just a fun side project and my first attempt at building a Chrome extension. Like many developers, I grabbed\n  the tools I knew and started building. Even though the tech stack has evolved over the years, I’m still happy with my\n  original choice.\n\u003C\u002Fp>\n\u003Cp>\n  Without making any major changes, the extension can sync all highlights with the cloud and the web app, and so far,\n  it’s been able to handle the needs of 100,000 users.\n\u003C\u002Fp>\n\u003Ch3 id=\"frontend\">Frontend\u003C\u002Fh3>\n\u003Cp>At the core of the Web Highlights Chrome extension are two primary front-end applications:\u003C\u002Fp>\n\u003Cul>\n  \u003Cli>\n    the\n    \u003Cstrong>extension\u003C\u002Fstrong>\n    itself, which is loaded into the user’s browser,\n  \u003C\u002Fli>\n  \u003Cli>\n    and the associated\n    \u003Cstrong>Web App\u003C\u002Fstrong>\n    that can be accessed at\n    \u003Ca href=\"https:\u002F\u002Fweb-highlights.com\" rel=\"noopener\">web-highlights.com\u003C\u002Fa>\n    .\n  \u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch4 id=\"browser-extension\">Browser Extension\u003C\u002Fh4>\n\u003Cp>\n  At the heart of\n  \u003Ca href=\"https:\u002F\u002Fweb-highlights.com\u002F\" rel=\"noopener\">Web Highlights\u003C\u002Fa>\n  is obviously the browser extension itself. This application part is loaded in the user’s browser, enabling text\n  highlighting on any web page and PDF.\n\u003C\u002Fp>\n\u003Cp>\n  It also displays a\n  \u003Cstrong>collapsible and expandable sidebar\u003C\u002Fstrong>\n  that shows all the user’s text\n  \u003Cstrong>highlights, notes, and tags\u003C\u002Fstrong>\n  .\n\u003C\u002Fp>\n\u003Cfigure class=\"kg-card kg-image-card kg-card-hascaption\">\n  \u003Cimg\n    src=\"\u002Fblog\u002Fcontent\u002Fimages\u002F2024\u002F09\u002Fimage-1.webp\"\n    class=\"kg-image\"\n    alt=\"Web Highlights Chrome Extension — Sidebar &amp; Highlighter\"\n    loading=\"lazy\"\n    width=\"1600\"\n    height=\"760\"\n    srcset=\"\n      \u002Fblog\u002Fcontent\u002Fimages\u002F2024\u002F09\u002Fimage-1.webp   600w,\n      \u002Fblog\u002Fcontent\u002Fimages\u002F2024\u002F09\u002Fimage-1.webp 1000w,\n      \u002Fblog\u002Fcontent\u002Fimages\u002F2024\u002F09\u002Fimage-1.webp            1600w\n    \"\n    sizes=\"(min-width: 720px) 720px\"\n  \u002F>\n  \u003Cfigcaption>\n    \u003Ca\n      href=\"https:\u002F\u002Fchrome.google.com\u002Fwebstore\u002Fdetail\u002Fweb-highlights-pdf-web-hi\u002Fhldjnlbobkdkghfidgoecgmklcemanhm\"\n      rel=\"noopener\"\n    >\n      Web Highlights Chrome Extension\n    \u003C\u002Fa>\n     — Sidebar &amp; Highlighter\n  \u003C\u002Ffigcaption>\n\u003C\u002Ffigure>\n\u003Cp>\n  Those who follow my articles will probably already guess that a large part of my front-end architecture is built using\n  \u003Cstrong>Web Components\u003C\u002Fstrong>\n  .\n\u003C\u002Fp>\n\u003Cblockquote>\n  Web Components are reusable client-side components based on official web standards. They are an excellent way of\n  encapsulating functionality from the rest of our code. Not only that, but you can reuse them in every web application\n  and web page.\n\u003C\u002Fblockquote>\n\u003Cp>\n  I have written many articles on how to build a Chrome Extension using Web Components. For example,\n  \u003Ca href=\"https:\u002F\u002Fweb-highlights.com\u002Fblog\u002Fbuilding-your-own-new-tab-chrome-extension\u002F\">\n    in this article I build a New Tab browser extension using Web Components\n  \u003C\u002Fa>\n  .\n\u003C\u002Fp>\n\u003Cfigure class=\"kg-card kg-bookmark-card\">\n  \u003Ca class=\"kg-bookmark-container\" href=\"https:\u002F\u002Fweb-highlights.com\u002Fblog\u002Fbuilding-your-own-new-tab-chrome-extension\u002F\">\n    \u003Cdiv class=\"kg-bookmark-content\">\n      \u003Cdiv class=\"kg-bookmark-title\">Building Your Own New Tab Chrome Extension\u003C\u002Fdiv>\n      \u003Cdiv class=\"kg-bookmark-description\">\n        How to build a beautiful New Tab Chrome Extension with plain HTML, CSS, JavaScript, and Web Components\n      \u003C\u002Fdiv>\n      \u003Cdiv class=\"kg-bookmark-metadata\">\n        \u003Cimg class=\"kg-bookmark-icon\" src=\"https:\u002F\u002Fweb-highlights.com\u002Ffavicon.ico\" alt=\"\" \u002F>\n        \u003Cspan class=\"kg-bookmark-author\">Web Highlights Blog\u003C\u002Fspan>\n        \u003Cspan class=\"kg-bookmark-publisher\">Marius Bongarts\u003C\u002Fspan>\n      \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n    \u003Cdiv class=\"kg-bookmark-thumbnail\">\n      \u003Cimg src=\"\u002Fblog\u002Fcontent\u002Fimages\u002F2023\u002F08\u002Fhabit-tracker.webp\" alt=\"\" \u002F>\n    \u003C\u002Fdiv>\n  \u003C\u002Fa>\n\u003C\u002Ffigure>\n\u003Cp>\n  Web Components offer numerous advantages, so I used them from the beginning of developing the browser extension. To\n  streamline the process of creating Web Components, I opted to utilize the\n  \u003Ca href=\"https:\u002F\u002Flit.dev\u002F\" rel=\"noopener\">\u003Cstrong>Lit library\u003C\u002Fstrong>\u003C\u002Fa>\n  .\n\u003C\u002Fp>\n\u003Cp>\n  This lightweight library, maintained by Google, is specifically designed for building web components and makes the\n  development process significantly easier.\n\u003C\u002Fp>\n\u003Cp>\n  I have discussed the benefits of Web Components in various articles previously. However, the most significant\n  advantage that saves me a lot of work is the\n  \u003Cstrong>reusability of components\u003C\u002Fstrong>\n  .\n\u003C\u002Fp>\n\u003Cfigure class=\"kg-card kg-bookmark-card kg-card-hascaption\">\n  \u003Ca\n    class=\"kg-bookmark-container\"\n    href=\"https:\u002F\u002Fmedium.com\u002F@mariusbongarts\u002Fwill-web-components-replace-frontend-frameworks-535891d779ba\"\n  >\n    \u003Cdiv class=\"kg-bookmark-content\">\n      \u003Cdiv class=\"kg-bookmark-title\">Will Web Components Replace Frontend Frameworks?\u003C\u002Fdiv>\n      \u003Cdiv class=\"kg-bookmark-description\">They are built to solve different problems.\u003C\u002Fdiv>\n      \u003Cdiv class=\"kg-bookmark-metadata\">\n        \u003Cimg\n          class=\"kg-bookmark-icon\"\n          src=\"https:\u002F\u002Fcdn-static-1.medium.com\u002F_\u002Ffp\u002Ficons\u002FMedium-Avatar-500x500.svg\"\n          alt=\"\"\n        \u002F>\n        \u003Cspan class=\"kg-bookmark-author\">Medium\u003C\u002Fspan>\n        \u003Cspan class=\"kg-bookmark-publisher\">Marius Bongarts\u003C\u002Fspan>\n      \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n    \u003Cdiv class=\"kg-bookmark-thumbnail\">\n      \u003Cimg src=\"https:\u002F\u002Fmiro.medium.com\u002Fmax\u002F1200\u002F1*YgC0g3TFf9QtY1jCnQwjrQ.png\" alt=\"\" \u002F>\n    \u003C\u002Fdiv>\n  \u003C\u002Fa>\n  \u003Cfigcaption>Will Web Components Replace Frontend Framework\u003C\u002Ffigcaption>\n\u003C\u002Ffigure>\n\u003Cp>\n  Thanks to their easy reusability, I can quickly integrate some components from the extension into the web app. For\n  instance, if you prefer React, you can easily\n  \u003Ca href=\"https:\u002F\u002Fweb-highlights.com\u002Fblog\u002Fhow-to-use-web-components-in-react\u002F\">\n    use your Web Components in your React app\n  \u003C\u002Fa>\n  . I’ll explain more about that in the next section.\n\u003C\u002Fp>\n\u003Cp>\n  I also use\n  \u003Cstrong>Typescript\u003C\u002Fstrong>\n  like in all of my other applications as well. Furthermore, I included\n  \u003Cstrong>Redux\u003C\u002Fstrong>\n  to manage and synchronize states within the application.\n\u003C\u002Fp>\n\u003Ch4 id=\"web-app\">Web App\u003C\u002Fh4>\n\u003Cp>\n  In addition to the Chrome extension, Web Highlights includes a progressive web app (PWA) allowing users to access\n  their saved highlights from any device.\n\u003C\u002Fp>\n\u003Cblockquote>\n  A\n  \u003Cstrong>progressive web app\u003C\u002Fstrong>\n  (PWA) is an app that's built using web platform technologies, but that provides a user experience like that of a\n  platform-specific app. -\n  \u003Ca href=\"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FProgressive_web_apps\">MDN\u003C\u002Fa>\n\u003C\u002Fblockquote>\n\u003Cp>Here is what the dashboard looks like:\u003C\u002Fp>\n\u003Cfigure class=\"kg-card kg-image-card kg-card-hascaption\">\n  \u003Cimg\n    src=\"\u002Fblog\u002Fcontent\u002Fimages\u002F2024\u002F09\u002Fimage-2.webp\"\n    class=\"kg-image\"\n    alt=\"Web Highlights dashboard\"\n    loading=\"lazy\"\n    width=\"1200\"\n    height=\"611\"\n    srcset=\"\n      \u002Fblog\u002Fcontent\u002Fimages\u002F2024\u002F09\u002Fimage-2.webp   600w,\n      \u002Fblog\u002Fcontent\u002Fimages\u002F2024\u002F09\u002Fimage-2.webp 1000w,\n      \u002Fblog\u002Fcontent\u002Fimages\u002F2024\u002F09\u002Fimage-2.webp            1200w\n    \"\n    sizes=\"(min-width: 720px) 720px\"\n  \u002F>\n  \u003Cfigcaption>\n    \u003Ca href=\"https:\u002F\u002Fweb-highlights.com\u002F\">Web Highlights\u003C\u002Fa>\n    dashboard\n  \u003C\u002Ffigcaption>\n\u003C\u002Ffigure>\n\u003Cp>\n  I decided to use Vue.js for the PWA somewhat on a whim. It was early in Vue.js’s rise, and there was a lot of\n  excitement around it. I was eager to learn it, which led me to start building the WebApp’s frontend with it.\n\u003C\u002Fp>\n\u003Cp>\n  Nevertheless, as already mentioned, I reuse many Web Components from the extension in Vue.js. This saves me a lot of\n  duplicated code and work. When looking at Web Highlights’ dashboard, you might have noticed that some components look\n  very similar to the ones in the extension.\n\u003C\u002Fp>\n\u003Cfigure class=\"kg-card kg-bookmark-card\">\n  \u003Ca\n    class=\"kg-bookmark-container\"\n    href=\"https:\u002F\u002Fweb-highlights.com\u002Fblog\u002Fthe-dangers-of-relying-too-much-on-frontend-frameworks-a-short-story\u002F\"\n  >\n    \u003Cdiv class=\"kg-bookmark-content\">\n      \u003Cdiv class=\"kg-bookmark-title\">The Dangers of Relying Too Much on Frontend Frameworks - A Short Story\u003C\u002Fdiv>\n      \u003Cdiv class=\"kg-bookmark-description\">\n        Frontend Frameworks can be powerful tools, but the balance between innovation and established web standards like\n        Web Components is key for staying competitive.\n      \u003C\u002Fdiv>\n      \u003Cdiv class=\"kg-bookmark-metadata\">\n        \u003Cimg class=\"kg-bookmark-icon\" src=\"https:\u002F\u002Fweb-highlights.com\u002Ffavicon.ico\" alt=\"\" \u002F>\n        \u003Cspan class=\"kg-bookmark-author\">Web Highlights Blog\u003C\u002Fspan>\n        \u003Cspan class=\"kg-bookmark-publisher\">Marius Bongarts\u003C\u002Fspan>\n      \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n    \u003Cdiv class=\"kg-bookmark-thumbnail\">\n      \u003Cimg src=\"\u002Fblog\u002Fcontent\u002Fimages\u002F2023\u002F08\u002F1_Z3PVTv7bSMnaMl4zPHrTsA--1-.webp\" alt=\"\" \u002F>\n    \u003C\u002Fdiv>\n  \u003C\u002Fa>\n\u003C\u002Ffigure>\n\u003Cp>\n  The reason for this is that they are essentially identical, except for the fact that they are loaded within my Vue.js\n  app. To illustrate this, let’s compare the screenshot from my extension with the Vue.js PWA and note their\n  similarities:\n\u003C\u002Fp>\n\u003Cfigure class=\"kg-card kg-image-card kg-card-hascaption\">\n  \u003Cimg\n    src=\"https:\u002F\u002Fcdn-images-1.medium.com\u002Fmax\u002F1600\u002F1*Mirny1taQgm0Sam6ee11vA.png\"\n    class=\"kg-image\"\n    alt=\"Web Components in Web App and Chrome Extension\"\n    loading=\"lazy\"\n  \u002F>\n  \u003Cfigcaption>Web Components in Web App and Chrome Extension\u003C\u002Ffigcaption>\n\u003C\u002Ffigure>\n\u003Cp>\n  As you can see, the rich editor, share button, and toolbar are used in both the web app and the Chrome extension. Many\n  other components are shared between the two as well. Hopefully, this comparison clearly shows the advantages of\n  reusing web components.\n\u003C\u002Fp>\n\u003Ch4 id=\"blog\">Blog\u003C\u002Fh4>\n\u003Cp>\n  Besides the frontend, I also run a blog under the subdomain\n  \u003Ca href=\"https:\u002F\u002Fweb-highlights.com\u002Fblog\" rel=\"noopener\">\u003Cstrong>web-highlights.com\u002Fblog\u003C\u002Fstrong>\u003C\u002Fa>\n  . The main motive for setting up the blog was to rank higher on Google.\n\u003C\u002Fp>\n\u003Cp>\n  In the last few weeks, I have dealt extensively with the topic of SEO and understood that a blog on one’s domain could\n  significantly improve the Google ranking. When it comes to SEO, then there is no way around it.\n\u003C\u002Fp>\n\u003Cp>\n  Hosting a blog and creating content on my website was a game-changer. If you want to learn more about it, read this\n  article:\n\u003C\u002Fp>\n\u003Cfigure class=\"kg-card kg-bookmark-card kg-card-hascaption\">\n  \u003Ca\n    class=\"kg-bookmark-container\"\n    href=\"https:\u002F\u002Fweb-highlights.com\u002Fblog\u002F3-instant-ways-to-increase-your-google-ranking\u002F\"\n  >\n    \u003Cdiv class=\"kg-bookmark-content\">\n      \u003Cdiv class=\"kg-bookmark-title\">3 Instant Ways to Increase Your Google Ranking\u003C\u002Fdiv>\n      \u003Cdiv class=\"kg-bookmark-description\">\n        Anyone can implement these three essential points to make their product visible on Google, Bing, and Co.\n      \u003C\u002Fdiv>\n      \u003Cdiv class=\"kg-bookmark-metadata\">\n        \u003Cimg class=\"kg-bookmark-icon\" src=\"https:\u002F\u002Fweb-highlights.com\u002Ffavicon.ico\" alt=\"\" \u002F>\n        \u003Cspan class=\"kg-bookmark-author\">Web Highlights Blog\u003C\u002Fspan>\n        \u003Cspan class=\"kg-bookmark-publisher\">Marius Bongarts\u003C\u002Fspan>\n      \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n    \u003Cdiv class=\"kg-bookmark-thumbnail\">\n      \u003Cimg\n        src=\"https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1635944095210-23114a1fb7c0?crop&#x3D;entropy&amp;cs&#x3D;tinysrgb&amp;fit&#x3D;max&amp;fm&#x3D;jpg&amp;ixid&#x3D;MnwxMTc3M3wwfDF8c2VhcmNofDF8fGljZWJlcmd8ZW58MHx8fHwxNjY3MDM4MjA0&amp;ixlib&#x3D;rb-4.0.3&amp;q&#x3D;80&amp;w&#x3D;2000\"\n        alt=\"\"\n      \u002F>\n    \u003C\u002Fdiv>\n  \u003C\u002Fa>\n  \u003Cfigcaption>Instant Ways to Increase Your Google Ranking\u003C\u002Ffigcaption>\n\u003C\u002Ffigure>\n\u003Cp>\n  When it comes to hosting a blog, there are several choices available. However, I opted for a self-hosted\n  \u003Cstrong>Ghost\u003C\u002Fstrong>\n  instance.\n\u003C\u002Fp>\n\u003Cblockquote>\n  \u003Cstrong>Ghost\u003C\u002Fstrong>\n  \u003Cem>\n    It comes with modern tools to build a website, publish content, send newsletters &amp; offer paid subscriptions to\n    members. — \n  \u003C\u002Fem>\n  \u003Ca href=\"https:\u002F\u002Fghost.org\u002F\" rel=\"noopener ugc nofollow noopener\">\u003Cem>https:\u002F\u002Fghost.org\u002F\u003C\u002Fem>\u003C\u002Fa>\n\u003C\u002Fblockquote>\n\u003Cp>\n  I created a digital ocean droplet and set up Ghost on it. This is easy, as Ghost and Digital Ocean provide excellent\n  \u003Ca href=\"https:\u002F\u002Fghost.org\u002Fdocs\u002Finstall\u002Fdigitalocean\u002F\" rel=\"noopener ugc nofollow noopener\">\n    getting-started articles\n  \u003C\u002Fa>\n  and\n  \u003Ca href=\"https:\u002F\u002Fmarketplace.digitalocean.com\u002Fapps\u002Fghost\" rel=\"noopener ugc nofollow noopener\">documentation\u003C\u002Fa>\n  .\n\u003C\u002Fp>\n\u003Chr \u002F>\n\u003Ch3 id=\"backend\">Backend\u003C\u002Fh3>\n\u003Cp>When choosing a server, it was essential to me that I could also use JavaScript\u002FTypescript. For two main reasons:\u003C\u002Fp>\n\u003Cul>\n  \u003Cli>\n    I simply\n    \u003Cstrong>know JavaScript best\u003C\u002Fstrong>\n  \u003C\u002Fli>\n  \u003Cli>\n    I can\n    \u003Cstrong>share code between the server and frontend\u003C\u002Fstrong>\n  \u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>\n  The second point is particularly important to me. The fact that I can reuse code in both the frontend and the backend\n  makes my code so much cleaner. In my article “\n  \u003Ca href=\"https:\u002F\u002Fmedium.com\u002F@mariusbongarts\u002Fshare-code-between-react-client-and-express-server-5dc0977faa76\">\n    Share Code between React Client and Express Server\n  \u003C\u002Fa>\n  ,” you can learn how to implement this using a\n  \u003Cstrong>mono repository\u003C\u002Fstrong>\n  .\n\u003C\u002Fp>\n\u003Cfigure class=\"kg-card kg-bookmark-card kg-card-hascaption\">\n  \u003Ca\n    class=\"kg-bookmark-container\"\n    href=\"https:\u002F\u002Fmedium.com\u002F@mariusbongarts\u002Fshare-code-between-react-client-and-express-server-5dc0977faa76\"\n  >\n    \u003Cdiv class=\"kg-bookmark-content\">\n      \u003Cdiv class=\"kg-bookmark-title\">Share Code between React Client and Express Server\u003C\u002Fdiv>\n      \u003Cdiv class=\"kg-bookmark-description\">Set up a mono repository with yarn workspaces\u003C\u002Fdiv>\n      \u003Cdiv class=\"kg-bookmark-metadata\">\n        \u003Cimg\n          class=\"kg-bookmark-icon\"\n          src=\"https:\u002F\u002Fcdn-static-1.medium.com\u002F_\u002Ffp\u002Ficons\u002FMedium-Avatar-500x500.svg\"\n          alt=\"\"\n        \u002F>\n        \u003Cspan class=\"kg-bookmark-author\">Medium\u003C\u002Fspan>\n        \u003Cspan class=\"kg-bookmark-publisher\">Marius Bongarts\u003C\u002Fspan>\n      \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n    \u003Cdiv class=\"kg-bookmark-thumbnail\">\n      \u003Cimg src=\"https:\u002F\u002Fmiro.medium.com\u002Fmax\u002F1200\u002F1*1rCMJTTZ8HBIad8K8H6Lfw.png\" alt=\"\" \u002F>\n    \u003C\u002Fdiv>\n  \u003C\u002Fa>\n  \u003Cfigcaption>Share Code\u003C\u002Ffigcaption>\n\u003C\u002Ffigure>\n\u003Ch4 id=\"main-server\">Main Server\u003C\u002Fh4>\n\u003Cp>\n  A huge part of my application forms one server, which relies on\n  \u003Cstrong>Nest.js\u003C\u002Fstrong>\n  . Nest.js is a powerful Node.js framework that provides a robust set of tools for building scalable and efficient\n  server-side applications.\n\u003C\u002Fp>\n\u003Cp>\n  The server synchronizes users’ highlights, bookmarks, tags, and notes with the cloud. This functionality allows users\n  to access their highlights not only within the extension but also from anywhere there is an internet connection. Users\n  can log in to the web app, which downloads all their highlights from the server. It is also possible to share your\n  research with friends who do not have the extension.\n\u003C\u002Fp>\n\u003Cp>\n  To ensure secure authentication, the server implements JSON Web Tokens (\n  \u003Cstrong>JWT\u003C\u002Fstrong>\n  ) technology. Furthermore, I integrated Google Sign-In with\n  \u003Cstrong>Firebase\u003C\u002Fstrong>\n  .\n\u003C\u002Fp>\n\u003Cp>\n  Furthermore, my server handles a few asynchronous processes I implemented with\n  \u003Cstrong>CRON\u003C\u002Fstrong>\n  jobs. For example, users can set email reminders that are then executed every hour in a CRON job.\n\u003C\u002Fp>\n\u003Cp>\n  To make some recurring queries faster and more efficient, I also introduced a caching system that simply uses the\n  \u003Ccode>NodeCache\u003C\u002Fcode>\n  to cache time-consuming database queries and return them faster.\n\u003C\u002Fp>\n\u003Ch4 id=\"micro-server\">Micro Server\u003C\u002Fh4>\n\u003Cp>\n  In addition to the main server, I implemented a small microservice using\n  \u003Cstrong>Express.js\u003C\u002Fstrong>\n  . This microservice exports HTML to PDF files using the\n  \u003Cstrong>Puppeteer\u003C\u002Fstrong>\n  library.\n\u003C\u002Fp>\n\u003Cp>\n  I had initially considered implementing this functionality in the main Nest.js server, but given the Puppeteer\n  library's large size and resource-intensive operations, I decided to offload it to a separate microservice. This way,\n  I can keep the central server lean and fast while still providing the necessary PDF export functionality via the\n  microservice.\n\u003C\u002Fp>\n\u003Ch3 id=\"database\">Database\u003C\u002Fh3>\n\u003Cp>\n  The Web Highlights Chrome extension relies on a\n  \u003Cstrong>MongoDB\u003C\u002Fstrong>\n  database to store user data. MongoDB is a popular NoSQL database with high scalability, availability, and performance.\n\u003C\u002Fp>\n\u003Cp>\n  I am using Mongoose, which is a popular object data modeling (ODM) library for MongoDB, to model the data in the\n  database.\n  \u003Cstrong>Mongoose\u003C\u002Fstrong>\n  provides a simple and intuitive API for defining schemas, creating queries, and validating data.\n\u003C\u002Fp>\n\u003Cp>\n  The database is hosted on\n  \u003Cstrong>MongoDB Atlas Cloud Database,\u003C\u002Fstrong>\n  which handles all the complexity of deploying and managing my database. Additionally, it offers the security of\n  automatic backups.\n\u003C\u002Fp>\n\u003Ch3 id=\"hosting\">Hosting\u003C\u002Fh3>\n\u003Ch4 id=\"server\">Server\u003C\u002Fh4>\n\u003Cp>\n  Currently, my main server and microservice are hosted on Heroku Dynos, which provide flexible hosting for my apps.\n\u003C\u002Fp>\n\u003Cp>\n  Heroku has been really convenient because it lets me easily deploy from GitHub and set up a pre-production testing\n  server by deploying from my development branch.\n\u003C\u002Fp>\n\u003Cp>\n  It might also be more cost-effective to host the main server on the Digital Ocean droplet, but I’m really happy with\n  Heroku. I like knowing I can easily restart the server from anywhere using their web app. It gives me peace of mind\n  knowing it works reliably and I can manage it, even when I’m on vacation for a few weeks, ensuring my server is always\n  running.\n\u003C\u002Fp>\n\u003Ch4 id=\"frontend-1\">Frontend\u003C\u002Fh4>\n\u003Cp>\n  I have set up a\n  \u003Cstrong>Digital Ocean droplet\u003C\u002Fstrong>\n  to host the front end of my Vue.js web app. This setup has changed a lot over the years. When talking about the front\n  end, I have to differentiate between the marketing website at\n  \u003Ca href=\"web-highlights.com\">web-highlights.com\u003C\u002Fa>\n  and the actual web app at\n  \u003Ca href=\"app.web-highlights.com\">app.web-highlights.com\u003C\u002Fa>\n  .\n\u003C\u002Fp>\n\u003Cp>\n  The marketing website is a server-side rendered Nuxt3 application, while the PWA is a client-side rendered Vue3 app\n  that even works offline.\n\u003C\u002Fp>\n\u003Cp>\n  A configured A-record in my domain provider,\n  \u003Cstrong>Strato\u003C\u002Fstrong>\n  , directs the domain\n  \u003Ca href=\"https:\u002F\u002Fweb-highlights.com\" rel=\"noopener\">\u003Cem>web-highlights.com\u003C\u002Fem>\u003C\u002Fa>\n  to the IP address of my Digital Ocean droplet.\n\u003C\u002Fp>\n\u003Cp>\n  The droplet is set up to serve the web app as a static site and the marketing website through the port of the running\n  Nuxt3 app. I also configured an NGINX server to host my Ghost CMS blog under the \u002Fblog subfolder of the domain. This\n  setup allows visitors to access both the web app and blog from the same domain easily.\n\u003C\u002Fp>\n\u003Ch4 id=\"payment\">Payment\u003C\u002Fh4>\n\u003Cp>\n  Since my app offers\n  \u003Ca href=\"https:\u002F\u002Fweb-highlights.com\u002Fpricing\">different paid subscription plans\u003C\u002Fa>\n  , I needed to choose a payment provider. Some of the most popular options are Stripe, Paddle, Lemon Squeezy, and\n  others.\n\u003C\u002Fp>\n\u003Cp>\n  I went with Paddle because, like Lemon Squeezy, they offer a Merchant of Record system that makes it easy to manage\n  payments globally. This is especially important for being legally compliant in European countries.\n\u003C\u002Fp>\n\u003Cp>\n  Integrating Paddle into my Nest.js backend was simple, thanks to their excellent documentation and easy-to-use sandbox\n  environment for testing.\n\u003C\u002Fp>\n\u003Ch3 id=\"testing-jest-cypress\">Testing: Jest + Cypress\u003C\u002Fh3>\n\u003Cp>\n  Initially, like many others, I developed my application without any unit tests or integration tests. However, over\n  time, I added a significant number of unit tests and integration tests, which have helped me avoid many issues.\n\u003C\u002Fp>\n\u003Cp>\n  Although it was challenging to test the Chrome extension fully integrated, I established a test environment where I\n  could launch and test my application locally. Using Cypress, I have tested almost every feature of my application.\n  Additionally, I have implemented many unit tests for complex logic, for which I utilize Jest.\n\u003C\u002Fp>\n\u003Ch3 id=\"final-thoughts\">Final Thoughts\u003C\u002Fh3>\n\u003Cp>\n  That's it! Of course, I didn’t cover every single technology or library I’m using, but I hope this gives you some\n  useful insights. Maybe you can even apply some of these ideas to build your own Chrome extension.\n\u003C\u002Fp>\n\u003Cp>Here’s a quick summary of the tech stack:\u003C\u002Fp>\n\u003Cul>\n  \u003Cli>\n    \u003Cstrong>Frontend:\u003C\u002Fstrong>\n    Web Components + Lit, Vue 3 PWA, Nuxt3, Typescript, Redux, Ghost CMS\n  \u003C\u002Fli>\n  \u003Cli>\n    \u003Cstrong>Backend:\u003C\u002Fstrong>\n    Nest.js, Express.js, JWT, Firebase Auth\n  \u003C\u002Fli>\n  \u003Cli>\n    \u003Cstrong>Database:\u003C\u002Fstrong>\n    MongoDB, Mongoose, MongoDB Atlas Cloud\n  \u003C\u002Fli>\n  \u003Cli>\n    \u003Cstrong>Payment\u003C\u002Fstrong>\n    : Paddle\n  \u003C\u002Fli>\n  \u003Cli>\n    \u003Cstrong>Hosting:\u003C\u002Fstrong>\n    Heroku, Digital Ocean, Strato\n  \u003C\u002Fli>\n  \u003Cli>\n    \u003Cstrong>Testing:\u003C\u002Fstrong>\n    Jest, Cypress\n  \u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>\n  I hope you enjoyed reading this article! I’m always happy to answer questions and open to feedback. Feel free to reach\n  out anytime 😊\n\u003C\u002Fp>\n\u003Cp>\n  I hope you enjoyed reading this article. I am always happy to answer questions and am open to criticism. Feel free to\n  contact me at any time 😊\n\u003C\u002Fp>\n\u003Cp>\n  Get in touch with me via\n  \u003Cstrong>\u003C\u002Fstrong>\n  \u003Ca\n    href=\"https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fmarius-bongarts-6b3638171\u002F\"\n    rel=\"noopener ugc nofollow noopener noopener noopener noopener noopener noopener noopener noopener noopener noopener noopener noopener noopener noopener\"\n  >\n    \u003Cstrong>LinkedIn\u003C\u002Fstrong>\n  \u003C\u002Fa>\n  \u003Cstrong>\u003C\u002Fstrong>\n  or follow me on\n  \u003Ca\n    href=\"https:\u002F\u002Ftwitter.com\u002FMariusBongarts\"\n    rel=\"noopener ugc nofollow noopener noopener noopener noopener noopener noopener noopener noopener noopener noopener noopener noopener noopener noopener\"\n  >\n    \u003Cstrong>Twitter\u003C\u002Fstrong>\n  \u003C\u002Fa>\n  . Also, check out my\n  \u003Ca\n    href=\"https:\u002F\u002Fchrome.google.com\u002Fwebstore\u002Fdetail\u002Fweb-highlights-pdf-web-hi\u002Fhldjnlbobkdkghfidgoecgmklcemanhm\"\n    rel=\"noopener\"\n  >\n    PDF &amp; Web Highlighter Chrome\n  \u003C\u002Fa>\n  extension if you haven’t.\n  \u003Ca href=\"https:\u002F\u002Fmedium.com\u002F@mariusbongarts\">Follow me on Medium\u003C\u002Fa>\n  for more articles like this.\n\u003C\u002Fp>\n","Marius Bongarts","https:\u002F\u002Fwww.facebook.com\u002FMariusBongartsWeb","\u002Fblog\u002Fcontent\u002Fimages\u002F2022\u002F08\u002FIMG_7861_Square.jpeg","https:\u002F\u002Fmarius-bongarts.de\u002F","Founder & Software Engineer","https:\u002F\u002Ftwitter.com\u002FMariusBongarts",2159,[42,77,93],{"id":43,"slug":44,"title":45,"description":46,"url":47,"publishedAt":48,"createdAt":49,"updatedAt":50,"featured":20,"readingTime":51,"tags":52,"imageUrl":57,"imageAlt":58,"faviconUrl":59,"publishedAtString":48,"category":60,"readTimeString":76},"688719cf8b4b770aa39407fc","my-simple-strategy-for-3-268-5-star-chrome-extension-reviews","My Simple Strategy for 3,268 5‑Star Chrome Extension Reviews","Get 37x more reviews than Grammarly with this simple strategy. Learn the Win-Ask Method to boost your Chrome extension ratings and user engagement.","https:\u002F\u002Fweb-highlights.com\u002Fblog\u002Fmy-simple-strategy-for-3-268-5-star-chrome-extension-reviews\u002F","2025-07-28T06:48:26.000+00:00","2025-07-28T06:33:51.000+00:00","2025-07-28T07:04:33.000+00:00",6,[53,54,55,56],"Marketing","Side Projects","Side Hustle","Chrome Extension","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1633613286991-611fe299c4be?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3wxMTc3M3wwfDF8c2VhcmNofDF8fHJhdGluZ3xlbnwwfHx8fDE3NTM2ODQ0MzR8MA&ixlib=rb-4.1.0&q=80&w=2000","Five yellow star-shaped objects placed diagonally on a pink and blue background.","https:\u002F\u002Fweb-highlights.com\u002Ffavicon.ico",[61,69],{"type":31,"title":62,"shortTitle":30,"icon":63,"description":64,"excerpt":65,"thumbnail":66},"Behind the Scenes of Web Highlights","rocket","Hey 👋 I am Marius. I started Web Highlights in 2019 as a tool just for myself and have been working on it alone since then. Learn from my mistakes and stories right from the beginning how I grew the Chrome Extension to 250K users without any marketing costs.","These posts are the honest build log of Web Highlights: what worked, what hurt, and what I’d do again. If you care about indie products, Chrome extensions, or SEO-led growth, you’ll find practical lessons alongside the personal story.",{"url":67,"alt":68},"https:\u002F\u002Fweb-highlights.com\u002Fimages\u002Fme.png","Me, Marius Bongarts, in front of sea in Portugal, Algarve",{"type":70,"title":71,"shortTitle":72,"icon":73,"description":74,"excerpt":75},"productivity","Browser Productivity & Chrome Extensions","Productivity","command","Curated extension lists, focus workflows, and tips for getting more from Chrome—always with an eye toward reading, capture, and learning.","Productivity isn’t only task managers. These articles cover the browser layer: extensions that protect focus, comparisons that save you from dead-end tools, and setup tips that keep your reading and capture stack lightweight but powerful.","6 min read",{"id":78,"slug":79,"title":80,"description":81,"url":82,"publishedAt":83,"createdAt":84,"updatedAt":83,"featured":20,"readingTime":51,"tags":85,"imageUrl":87,"imageAlt":88,"faviconUrl":59,"publishedAtString":83,"category":89,"readTimeString":76},"671f386db98706751d677b5f","how-to-show-a-link-preview-on-any-website","How To Show A Beautiful Link Preview On Any Website","Easily add link previews to any website using the open-source Previewbox library. Works with any framework, fetching Open Graph data automatically.","https:\u002F\u002Fweb-highlights.com\u002Fblog\u002Fhow-to-show-a-link-preview-on-any-website\u002F","2024-10-29T20:25:55.000+00:00","2024-10-28T07:08:29.000+00:00",[24,86],"SEO","https:\u002F\u002Fweb-highlights.com\u002Fblog\u002Fcontent\u002Fimages\u002F2024\u002F10\u002F0_dkKw8Q1T3dUhNBo7.jpg","Lake with mountains in background",[90,92],{"type":31,"title":62,"shortTitle":30,"icon":63,"description":64,"excerpt":65,"thumbnail":91},{"url":67,"alt":68},{"type":70,"title":71,"shortTitle":72,"icon":73,"description":74,"excerpt":75},{"id":94,"slug":95,"title":96,"description":97,"url":98,"publishedAt":99,"createdAt":100,"updatedAt":101,"featured":20,"readingTime":102,"tags":103,"imageUrl":105,"imageAlt":106,"faviconUrl":59,"publishedAtString":99,"category":107,"readTimeString":110},"66ed6878e07b961e2f231a8f","lessons-from-a-bamboo-tree-staying-motivated-on-my-side-project","Five Years to 100K Users: The Bamboo Tree Story for Side Project Growth","Discover how a bamboo tree can inspire solopreneurs to stay motivated on side projects. Learn the value of delayed gratification on the journey to success.","https:\u002F\u002Fweb-highlights.com\u002Fblog\u002Flessons-from-a-bamboo-tree-staying-motivated-on-my-side-project\u002F","2024-09-22T20:17:54.000+00:00","2024-09-20T12:20:08.000+00:00","2024-09-24T07:00:43.000+00:00",7,[30,104,54,55],"Story","https:\u002F\u002Fweb-highlights.com\u002Fblog\u002Fcontent\u002Fimages\u002F2024\u002F09\u002F0_JjFAqJ78cyJMViqZ.webp","Tall Bamboo Tree",[108],{"type":31,"title":62,"shortTitle":30,"icon":63,"description":64,"excerpt":65,"thumbnail":109},{"url":67,"alt":68},"7 min read"]