How to make a webring

(With no JavaScript whatsoever, using Github, Jekyll, and Netlify)

Webrings are cool! However, I notice some webring tutorials are entirely dependent on JavaScript to make the webring function. That's not good; JavaScript should have a fallback as gracefully as possible, and "nonfunctional" is not a graceful fallback.
To counter this, I'm making my own webring tutorial. This is based on the framework I built for my Cuddler Webring.

For this tutorial, you need a GitHub account and a Netlify account. Netlify also supports GitLab, BitBucket, and Azure DevOps, so you can use one of those instead, but some URLs may need to change. This tutorial assumes you want users to be able to join via pull request, but if you don't, you could keep the repository on Netlify itself.
It is very helpful to be familiar with HTML.
It is helpful but not necessary to be familiar with Jekyll.

Create a GitHub repository (it can be private so you can experiment around with stuff!).

Files you need in the repository


source ""
gem 'jekyll'



These are just mandatory files Netlify needs to be able to use Jekyll.


url: https://insert-your-url-here.invalid # replace with your webring's URL, which should end in (no slash afterwards)
include: [_redirects]
 syntax_highlighter: +nil+
 smart_quotes: "39,39,34,34"
github_repo_url: https://insert-repository-url-here.invalid # replace with your repository's URL, which should begin with and should also have no slash at the end

Another mandatory Jekyll file, but you can customize it. Make sure to replace the URLs with your webring's URL and repository.
This one makes sure to include the _redirects file (discussed later), and disables syntax highlighting and smart quotes.


member1,"Member 1's website",
member2,"Member 2's page of stuff, things, and thingstuff",
member3,"Member three's *amazing* website of ""><🏳️‍🌈
random text characters!",

This file contains information about the members, which Jekyll can automatically use to generate a members list and a list of redirects. Change the lines after the first line to your members' information, using the comma-separated values format.
slug has to be unique for each member, and contain only ASCII lowercase letters, digits, and hyphen-minuses.


<!DOCTYPE html>
<html lang=en>
<meta charset=utf-8>
<link rel=stylesheet href={{'main.css'|relative_url}}>
<a href={{'/'|relative_url}}>Index</a><br>
<a href={{site.github_repo_url}}>View on GitHub</a>

This is mostly recognizable as HTML, with some key differences. Jekyll uses the front matter (those hyphen-minuses at the beginning) to indicate that it should parse the file. Jekyll will interpret the instructions between the {{ and }}, and replace them with the result.
This is a layout, markup we want used more than once.
If your webring is in a language other than English, you should update the lang=en accordingly.


title: Insert Title Here
layout: html
<h1>Insert Heading Here</h1>
<p>Insert a description of the webring here. Maybe link to an explanation of what a <a href=>webring</a> is.<br>
Perhaps link to a page explaining how to <a href="{{'/join'|relative_url}}">join the webring</a> (shown later in the tutorial)

<p>If the ring is broken, please <a href={{site.github_repo_url}}/issues/new>submit an error report on GitHub</a>.

<h2 id=members>Members ({{}})</h2>
{% for member in %}
<li><a href="{{member.url | xml_escape}}">{{ | xml_escape | newline_to_br}}</a>{% endfor %}

You should most certainly replace the placeholder text there.
This HTML file has front matter, which specifies a title and that it uses the html layout. Jekyll will first follow the instructions it's given between the {% and %}, and between the {{ and }}, then apply the html layout to it.
The for loop at the end of the page prints a list of all the members from _data/members.csv.


layout: none
{% for member in %}
{% if forloop.last %}
/{{member.slug | slugify}}/next  {{}}  302
/{{ | slugify}}/previous  {{member.url}}  302
{% break %}
{% endif %}
/{{member.slug | slugify}}/next  {{[forloop.index].url}}  302
/{{[forloop.index].slug | slugify}}/previous  {{member.url}}  302
{% endfor %}

This file is what gives the webring its actual ring functionality. For every slug in _data/members.csv, Netlify will redirect /slug/next and /slug/previous to the next and previous urls.

join.html (if you want users to be able to join)

title: How to Join
layout: html
<h1>Insert a relevant h1 here</h1>
<h2>Criteria to join</h2>
<li>Insert the criteria to join the webring here.
<li>I suggest a policy on clearly marking any NSFW content.
<li>and having no hate speech or bigotry.
<li>and not being advertisement-focused.

<small>It is impossible to enumerate everything that is not allowed. For this reason, we (the webmasters) reserve the right to remove a site if we feel it is outside of our own personal bounds.</small>

<h2>Add yourself to the members list</h2>
<p>Add a unique slug, your webpage's name, and your webpage's URL to <a href={{site.github_repo_url}}/blob/main/_data/members.csv>the members list on GitHub</a> in a pull request.<br>
Pull requests can also be used to change your existing entry or delete your entry.</p>

<summary>If you don't have a GitHub account, fill out this form instead:</summary>
<form name=signup method=POST data-netlify=true netlify-honeypot=bot>
<div hidden><label>Leave this blank unless you're spam <input name=bot></label></div>
<label>Slug <input name=slug pattern=[0-9a-z](?:-?[0-9a-z])* required></label>
(ASCII lowercase letters, digits, and hyphen-minuses)<br>
<label>Name <textarea name=name></textarea></label><br>
<label>URL <input name=url type=url></label><br>
<label>E-mail address <input type=email name=email required></label><br>You'll receive an e-mail when your addition/change is made.<br>
<input type=submit>
If you're editing your existing entry, make sure to use the same slug. If you're deleting your existing entry, leave the URL field blank.

<h2>Add links on your webpage</h2>
<p>Once you're on the members list, add links on your webpage to <b>{{'/YOUR-SLUG/next'|absolute_url}}</b> and <b>{{'/YOUR-SLUG/previous'|absolute_url}}</b>, replacing <b>YOUR-SLUG</b> with the slug you chose.<br>
Feel free to include a link to <b>{{'/'|absolute_url}}</b> as well.<br>
Check if <a href=>the <code>&lt;aside&gt;</code> element</a> is right for you.

This is a page instructing users how to join.
Users are given a link to the members list on GitHub, or, if they don't have a GitHub account, a form to fill out. The form will automatically be collected using Netlify Forms.
Users are then told which links to add to their webpage, and informed about the <aside> element, because I think the <aside> element is important for webring members to know about.
If you choose to use the form's e-mail address input, there is no automated system to send e-mails. You'd have to send them manually.

Of course you can change this file in any way you want, just like the others.

404.html (optional)

title: Not Found
layout: html
<h1>404 Not Found</h1>
<p>If you got here by clicking a link, somebody didn't configure their part of the webring correctly, and <a href={{site.github_repo_url}}/issues/new>submitting an error report on GitHub</a> is recommended.

This page will be displayed if a user tries to access a page which doesn't exist. In that case, they'll be told to submit an error report. If you don't include this file, users who try to access nonexistent pages will just be shown Netlify's default Not Found page.

This is a Cascading Style Sheets file, which you can use to make the webring not be generic black serif text on a white background. Customize the CSS in any way you want! I provide no default!

Publish the webring

Start a new Netlify site from the repository. Make sure to set the domain to the same one you have in _config.yml, and make sure you have the following build settings:

Base directory
Not Set
Build command
bundle exec jekyll build
Publish directory

If all goes well, you should see your webring when you navigate to your domain, and each member will be able to use their links as described!

If your GitHub repository is still private, make sure to set it to public now.