Régiolangues: a collaborative resource portal

Interested in French regional languages, my brother thought it was difficult to find resources on the web.

Homepage screenshot
Homepage screenshot


Régiolangues is a collaborative portal for French regional languages related resources.

This website has a bunch of cool features:

  • Advanced filtering
  • Pagination
  • Interactive map
  • Contribution forms
  • A CMS

For this project, I’m in charge of designing the interface, its database and coding the website. My brother is in charge of the content.

Here are the techs I used:

And probably a lot more I’ve forgotten about!

Check out the website!

Purpose and Goal

My brother enjoys Provençal (a French regional language) but felt that it was difficult to learn it online, that there were not really websites concentrating learning content and resources. So he asked me to build a website for this. The goal was to be able to have many languages so that people can contribute.

Spotlight: interactive map

There are many interesting parts in this project but let’s focus on what was the most challenging part: the interactive map.

Screenshot of the map
Screenshot of the map


For rendering the map itself, I used React Leaflet which is used a convenient wrapper for Leaflet. I’ve also used plugins such as react-leaflet-cluster that were really useful.

All styling (including the markers) is achieved with Tailwind CSS. Regarding the markers, icons are provided through the Directus CMS (as identifiers) and rendered using Iconify.

Finally, as for all the website, state is managed using Jotai which allowed me to filter things easily.

The custom markers’ nightmare

When using Leaflet it’s possible to use custom markers. They’re usually only custom images but in my case, I needed to render a React component. And it was not that easy…

1st attempt: renderToString

The first way to do this is by using renderToString from react-dom/server. And it actually works pretty well! It just compiles a React component to a string. For example:

import React from 'react'
import { renderToString } from 'react-dom/server'

function MyComponent({ children }: { children: React.ReactNode }) {
    return <div className="some-class">My component</div>

const renderedComponent = renderToString(
    <MyComponent>Hello there!</MyComponent>


<div class="some-class">Hello there!</div>

However, you loose any client side interactivity. And that was pretty critical because I was using the Icon component from @iconify/react!

2nd attempt: createRoot().render

So I tried to so some kind of custom rendering. It was really bad, it worked only half of the time. It was an absolute mess. I thrown it away pretty fast.

If you wanna have a look at this monstrosity, check it out here.

3rd attempt: renderToString… and web components!

The real issue there is that a custom Leaftlet marker expects a Leaftlet divIcon, which itself expects an html prop as a string like so:

function SomeComponent() {
    return (
                html: '', // this is what we are talking about

And I need client-side logic for iconify to run (it fetches icon client-side to avoid downloading all the icons). But there’s actually a way to do this, without React: web components!

So I extracted my custom marker icon to a component:

// MarkerIcon.tsx
import type { IconifyIconAttributes } from 'iconify-icon'
import type { SVGProps } from 'react'
import { Circle, HandsupFlower } from './Markers'
import 'iconify-icon' // imports the web component

// make sure TypeScript doesn't complain because
// iconify-icon is not a valid html tag by default
declare global {
    namespace JSX {
        interface IntrinsicElements {
            'iconify-icon': IconifyIconAttributes &
                Omit<SVGProps<SVGElement>, 'className'> &
                Partial<{ class: string }>

export default function MarkerIcon({ icon }: { icon: string }) {
    return (
                class="z-10 h-5 w-5 text-white"

I can then render it as string:

import L from 'leaflet'
import { Marker } from 'react-leaflet'
import { renderToString } from 'react-dom/server'
import MarkerIcon from './MarkerIcon'

function CustomMarker({
}: {
    icon: string
    position: [number, number]
}) {
    return (
                html: renderToString(<MarkerIcon icon={icon} />),

What about SEO?

The downside with a map like this is that it’s rendered client-side, so I’m loosing all the content associated with each point (viewable on click in a slideover). And here the solution is pretty easy: there is no way to have this content referenced.

So I just created another view: a standard list!

Current status

Most of the development is done right now, there are only a few things like notify CMS authors when contributions are made. Nothing crazy!

Lessons Learned

This project taught me many things:

  • Using the right tool for the right job: This is actually the 2nd version of RégioLangues. First one was built like a recursive file explorer view (Supabase, Nuxt 2, prerendering every 20min with cron) and it was not good. What was wrong above all was the mental model: recursive resources and categories VS a list of resources with tags
  • Dealing with maps using Leaflet
  • A new headless CMS, Directus!

Get in touch

Feel free to reach out if you're looking for a developer, have a question or just want to connect.