Why and How We Made Our Website Privacy Friendly

Florian FerbachFrançois Zaninotto

During a recent internal forum, we discussed about work ethics and our role in user data protection. We came to the conclusion that we would refuse to develop features with unmotivated user data collection and retention. We then realised that our own website was full of privacy problems. When we built it a few years ago, we simply included the "usual package" without worrying about your privacy, and we're sorry. We had to fix that.

Also, we're a French company. GDPR protects us as users, but as publishers, it forces us to get approval from visitors. We didn't want to cripple the user experience of marmelab.com with those ugly modals you see everywhere.

Our best option was to remove all data collection on our site. Read on to see how we did it.

Google Analytics

Google Analytics (GA) almost comes default with any website nowadays. We used it to determine how many people visited our website every day, and which page was visited the most. It helped us to motivate blog post authors, and to improve our SEO and social networking practices.

But once the traffic grows, it becomes an addiction. Where do we stop? How many visitors is enough? Should we stop sharing our stories if the rest of the world has no interest in them? The initial purpose of our blog was to share our learnings between us. Sharing them with the rest of the world was a way to boost the quality requirements. It was never the principal objective. As for react-admin (a large share of our incoming traffic), it's a free project. Additional visitors only mean more work (in support, bug triaging, etc), but no additional revenue.

All in all, the analytics had become an ego metric. And the dark side is that they gave Google the opportunity to collect much more data than the only "raw count" we were interested in.

Google Analytics shut down

So let's say that we have enough visitors and that we don't need to increase our traffic further. After all, always trying to grow is one of the killing trends of our world, right?

We decided to drop all tracking on our website. Instead of relying on visits, we now count on you to let us know what you like most, through comments and social sharing.

There is a risk that traffic on our site declines, because our brand awareness declines. We won't see it on google analytics, but we'll see it on our sales.

Google Fonts

Montserrat and Open Sans look great on our site. And Google Fonts is a fabulous service to download just the font format for your OS and browser. But as the fonts were loaded straight from Google CDN, it was still an opportunity for Google to collect your data.

The fonts are now served from our domain. Your browser will only download the font it supports thanks to CSS.

Bye, Google!


We used Disqus as our comments solution. It's very handy because we don't need any backend, and we can serve this very site completely statically.

But Disqus has poor user data protection policies.

We needed to do better, and thought about completly deleting comments and inviting people to mention us on Tweeter instead. We quickly realised it could ruin accessibility and prevent people from sharing because they don't have a Twitter account. So we searched further.

That's when we stumbled upon Commento. A privacy focused comment solution, that has actually respectful user data protection policies. It's not free, but operating a comment system costs money - that is, if you don't sell visitor data.

Commento has an "import from Disqus" feature, so none of the past comments were lost. They allow for UI customization, so the comments blend in the blog pages.

We implemented Commento rather quickly:

class Commento extends Component {
    componentWillMount() {
        if (typeof document !== 'undefined') {
            const scriptTag = document.createElement('script');
            scriptTag.type = 'text/javascript';
            scriptTag.async = true;
            scriptTag.src = `https://cdn.commento.io/js/commento.js`;
                document.getElementsByTagName('head')[0] ||
    render() {
        return <div id="commento" />;

export default Commento;

(Yes, we could have used refs.)

Bye, Disqus!

Embeded Content Jungle, on fire, with acid rain, bare foot

We thought we were done... Were we wrong...

As this is a rather technical blog, we often embed content such as tweets, codesandboxes, videos, or even simple images from Flickr by instance.

All of these are opportunities for third party services to collect some of your data, without you even noticing.

We now have more than 300 blog posts, and many have embeded content on them. We couldn't self host so many different things. Can you even self-host a code sandbox?

We decided to let you choose if you want to load remote content, or not, instead of forcing it to you.

embed video illustration

Our website is powered by Gatsby.js and Markdown. It means that having the behaviour we want could be quite complicated, technically.

Thanksfully, we can enable mdx support on gatsby. MDX is like Markdown, but with JSX (React components) support.

To embed a tweet, we went from the embed code suggested by Twitter:

<blockquote class="twitter-tweet">
  <p lang="en" dir="ltr">
    Already applied on the marmelab web site! <a href="https://t.co/CLaXPVCh3H">https://t.co/CLaXPVCh3H</a>
  </p>&mdash; marmelab (@marmelab) <a href="https://twitter.com/marmelab/status/1216715718122778626?ref_src=twsrc%5Etfw">January 13, 2020</a>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

To the render of a simple React component:

<Tweet id="1216715718122778626" />

This gave us the opportunity to do whatever we wanted in the Tweet react component. Like enhancing it with a PrivacyConsent component (the thing you see instead of the embeded content at first):

const PrivacyConsent = ({ consentKey, placeholder, children }) => {
    const [loaded, setLoaded] = useState(false);
    const [consent, setConsent] = useSessionStorage(consentKey, false);
    const [permanentConsent, setPermanentConsent] = useLocalStorage(

    const alwaysAllow = event => {

    const resetSettings = event => {

    const shouldShow = consent || permanentConsent;
    if (!shouldShow && loaded) {

    return (
            {!loaded && (
                    <Link href="#" onClick={alwaysAllow}>
                        Always allow
            {shouldShow && (
                <AppearContainer className={loaded ? 'loaded' : ''}>
                    <Link href="#" onClick={resetSettings}>
                        Reset privacy settings

We reviewed all the already published articles to replace embeds with our React components. It was long and tedious. If we missed any, don't hesitate telling us.

Bye, everything else!


We managed to make our website not gather any data at all, if you want it not to. It took some time, but we think it was well worth it. No cookie banner, no GDPR barrier, no free delivering of your data to internet giants, no doubt about our integrity. You don't have to trust us with your private data, since we don't collect any. You can check by yourself in the Network tab of your browser developer tools.

On the way to full user data privacy, we lost a few insights, like how many people read our articles. But we believe we don't need them, and that it helps us focus on sharing what we learn, instead of sharing for exposition.

We also made clear how we use your browser and your data, and what for, in our legal page.

We've protected your private data, dear reader, just like we would like ours to be protected everywhere.

Did you like this article? Share it!