Build a Membership Blog with Magic and 11ty

Magic Staff · April 27, 2021
Build a Membership Blog with Magic and 11ty

In this Guide, we’ll learn how to build a simple membership blog that uses 11ty as the static site generator and Magic as the auth solution.

Here’s a demo of the app!

#Quick Start

  1. git clone
  2. cd magic-11ty
  3. npm install
  4. Grab your Live Publishable API Key from your Magic Dashboard and paste it into header.njk.
  5. npm start

Note: When moving to production, we recommend that you whitelist your domain for your Publishable API Keys on the Magic Dashboard so that your API keys only work on your domain.

#What's 11ty?

11ty is a simpler static site generator. It was built for those wanting to build sites that’ll require only some static landing pages and a few lines of JS, and maybe a blog to generate. It uses the timeless idea of templating for component libraries. What’s great about 11ty is that we can choose the HTML templating language of our choice and it handles all of the complicated routing and page generation for us!

#What's Magic?

Magic is a plug and play SDK that enables passwordless authentication using magic links. If you want to learn how to use Magic to authenticate users into an Eleventy-powered blog, read on!

#Magic x 11ty

#Set up our pages with 11ty

Shout out to Stephanie Eckles and her tutorial, Build an Eleventy Site from Scratch. It’s a simple and straightforward tutorial that teaches you how to build a blog with 11ty. Feel free to follow her tutorial to get started with 11ty. With a few tweaks to the sample app provided by Stephanie, we can customize the blog to fit our use case. Here are main pages of our site:

  1. Login
  2. Profile
  3. Home

Let's explore each of them.


This is the login page. It’s where the user will be authenticated using the Magic Client SDK for Web.

magic-11ty-blog > src >


Here we’re using loginWithMagicLink to authenticate the user with their email address. It returns a promise that is resolved when the user clicks the Magic link that’s emailed to them. The resolved value is a Decentralized ID token named didToken which has a default 15-minute lifespan. Receiving the didToken confirms that the user has been authenticated by Magic.


This is the user profile page, it’s where the user is routed to once they’ve logged in.

magic-11ty-blog > src >


The email address and public address is retrieved using Magic’s getMetadata method. The public address can be thought of simply as the user’s unique ID provided by Magic.


This is our home page. It’ll have a list of all the blogs we’re fetching from a mock headless CMS. If you want to learn about how we’re sourcing this data, watch Stephanie’s quick tutorial.

magic-11ty-blog > src >


Because we only want logged in users to have access to this page, we’re using Magic’s isLoggedIn method to check whether or not they’re logged in. This method returns true if they’ve been authenticated by Magic, otherwise it will return false.

#Set up header.njk

#Get and Init Magic SDK

You’re probably wondering how we were able to access the Magic SDK across our .md files. The answer is in our magic-11ty-blog > src > _includes > header.njk file! It’s a pretty big Nunjucks file, so let’s go through it one code block at a time.


💡 Nunjucks: A rich and powerful templating language for JavaScript. It offers a complete feature set that makes templating a breeze.

First of all, this is where we’re getting and initializing the Magic SDK with the Publishable API Key we obtained from the Magic dashboard.

01<!-- 1. Get Magic SDK -->
02<script src=""></script>
03<!-- 2. Initialize Magic -->
05 const magic = new Magic("YOUR_PUBLIC_KEY");

Next, you’ll see that I’ve added a loader element to help create a better UX while we wait for the Magic SDK to load.

01<!-- 3. Add loader to wait for Magic SDK -->
02<div id="loader" class="loader">
03 <img src="" class="loaderImage"/>

#Add Navigation Links

Here are our navigation links! We’ll need to display each one of them depending on whether or not the user is logged in, which is why they each have a unique id.

01<!-- 4. Create our Navigation links -->
02<a id="home" href="/">Home</a>
03<a id="profile" href="/profile">Profile</a>
04<a id="login" href="/login">Login</a>
05<button id="logout" onclick="logout()">Logout</button>

As you can see, we’ve specified which pages are private and which are public. We’ve also added references to each of the Navigation links so that we can hide them accordingly.

01<!-- 5. Update visible pages depending on whether or not the user is logged in -->
03let privatePages = ["/profile/", "/"];
04let publicPages = ["/login/"];
06const loginElement = document.querySelector("#login");
07const profileElement = document.querySelector("#profile");
08const logoutElement = document.querySelector("#logout");
09const homeElement = document.querySelector("#home");
10const loaderElement = document.querySelector("#loader");

#Write getUser()

Here we write a function called getUser() which uses Magic’s isLoggedIn method to check if a user is currently logged in to the Magic SDK. Depending on the outcome, we will display certain navigation links.

01// Get the user & check whether or not they are logged in.
02// Show or hide pages depending on the outcome.
03const getUser = async () => {
04 // Gets the page user is currently on
05 const currentPath = window.location.pathname;
07 // Checks if user is currently logged into the Magic SDK
08 const isLoggedIn = await magic.user.isLoggedIn();
10 // If the user is logged in...
11 if (isLoggedIn) {
12   // ...prevent them from accessing public pages.
13   if (publicPages.includes(currentPath)) {
14     window.location.replace("/");
15   } else {
16     // Or hide links they don’t need to see
17 = "none";
18     // Get rid of the loader once the user reaches the correct page
19 = "none";
20   }
21 } else {
22   // If a logged out user tries to to access a private page, send them back to the login page
23   if (privatePages.includes(currentPath)) {
24     window.location.replace("/login");
25   } else {
26     // Hide links logged out users shouldn't be able to see
27 = "none";
28 = "none";
29 = "none";
30     // Get rid of the loader once the user reaches the correct page
31 = "none";
32   }
33 }

#Write logout()

The last important piece of code to see in header.njk is our logout() function! We use Magic’s logout() method to log out the currently authenticated Magic user. Once the user has been logged out, they’ll be taken back to the login page.

01<!-- 6. Log out the currently authenticated Magic user -->
03 const logout = async () => {
04   try {
05     await magic
06       .user
07       .logout();
08     window
09       .location
10       .replace("/login");
11   } catch (error) {
12     // Handle errors if required!
13     console.log('Ran into this error while logging out: ', error);
14   }
15   console.log('WHOO! User has logged out of all Magic SDK sessions.');
16 }


Alright, that’s it for today. Thank you for reading! If you have any questions, please ask them in the comments section below, or in our Discourse Forum. Until next time!

Let's make some magic!