<< Go back to Tech

Managing my Neocities Website

When you are not a front-end developer, finding the good tools is a nightmare. Here I present the different tools I use to manage my website.


In this post, I present the different tools I used to create and manage my website. Now, my current setup is Jekyll + my custom CLI, which allows me to convert simple markdown pages into nice html pages.

Table of content

Managing my Neocities Website

Programming is not innate. You don’t know when you start what are the wonderful libraries that would let you save time.

The first thing I known is that I am not a front-end developer. I have knowledge in algorithmic, but none on website development. I know basic stuff about HTML, which are just enough to start with.

Starting with a template

So, I start by searching an HTML template that fit my taste. I searched for a free one on the web, and got this one.

You can search your own there:

Be careful with the license: free doesn’t mean you can do anything you want with.

Now, you need to custom it a little bit, changing colors, columns location, fonts, …

Filling automatically your template

You cannot fill by hand each page, link to all images without error. So I used pandoc to compile markdown pages into HTML pages. This was okay for building independent pages, but not to link them together.

Using Automated Tools

A few month ago, I wanted to upgrade my website, to have “blog features” like a set of linked pages which display articles header. My current company has a technical blog which used Jekyll to generate pages. It is very hard to start with when there is an existing codebase. But starting from scratch with it, it was very easy.

You cannot migrate your website in one shot, but progressively by learning how to use the different features:

  1. Template pages and Layout: Within Jekyll, you need to create “layout pages”. This are template pages which after compilation would place the markdown content to the right place. To use your HTML template, you need to combine them together
  2. Learning the directory structure: When starting coding, for me everything needed to be flat, i.e. all the material within the same folder. However, after some time, it becomes a big mess. Then, I saw that many people use standard names like lib, src, img, etc. But how do you know which one you need to select ? Jekyll has a directory structure that help you to get everything organized.
  3. Using the yaml metadata to enrich pages.
  4. Understanding post: Your website might be a bunch of page that you update periodically. Or it could be a blog where you add new pages every weeks. The best option is to store the pages into the _post folder, where jekyll will take care of it.
  5. Using CLI to update your website.


Jekyll is a tool that allows to transform easy-to-write markdown files into HTML pages. Additionally, it allows to run your website locally, so you can see the direct result.

To start with, I recommand to test their examples to understand how it works progressively.

I won’t detail the basic exemples, but some more advanced that are less documented on the web. (Their documentation is a good start, but you may like additional examples).


Posts are particular markdown files located in _post/ with the syntax yyyy-mm-dd-Whatever_name_you_like.md. You can custom this format, however it is good enough to start with.

In the yaml header, you need to specify the category. For instance category: foo would lead to the creation into _site/ of _site/foo/yyyy/mm/dd/Whatever_name_you_like.html

If you do not want them to be published yet, write them into _draft/ folder. They won’t be transformed into html.

Filtering by Categories

categories and category are reserved names.

On a post, you can set one category

  layout: project_page
  title: This is a test page.
  category: projects
    - tag number 1
    - other tag
    - last tag

You may want to list all items related to it.

In another page, using the Liquid syntax:

  tag: project

  {% for post in site.posts %}
    {% if post.categories contains page.tag %}
    <a href="{{ post.url }}">{{ post.title }}</a>
    {% endif %}
  {% endfor %}

Note that it could be page.tag, but I could also rename tag: project into filter_keyword: project and search for page.filter_keyword.

Filtering by tags

When you write a post, you can often classify it into many topics, so it is hard to choose a single one. Instead, you could add into your metadata a list of tags that best describe your page.

Here, I propose an example to list within a single page all posts with a particular tag in.

Suppose you have a blog post where you set in the yaml header:

    - web
    - programming
    - blog

Then, on a regular page, you can list all the post about a particular topic.

  tagx: programming

List of post with "{{ page.tagx }}" included:

  {% for post in site.posts %}
    {% if post.tags contains page.tagx %}
    <a href="{{ post.url }}">{{ post.title }}</a>
    {% endif %}
  {% endfor %}

Here I names tagx the tag to find, to show the difference with tags which is a list.

Listing your Posts

In many blogs, you have some pages where you can preview 5 post at a time, with a clic button to go to the next page or the previous one, listing other blog articles. Here, I show you how to create automatically this n-linked page structure.

This is hard to setup, you need some time to fix the mistakes. See the official documentation for more info.

First, in the _config.yml, you need to configure the behavior:

  - jekyll-paginate-v2

url: "" # the base hostname & protocol for your site, e.g. http://example.com
baseurl: "" # the subpath of your site, e.g. /blog
  enabled: true
  per_page: 6
  permalink: '/page:num/'
  sort_reverse: true

You need of course to install jekyll-paginate-v2.

This config does not create anything for the moment. In the root directory, you need to instantiate the location of these blogposts listers. So I added one: mkdir blg_project/

Plus, you need to add an HTML file to say what you want in.

vim blg_project/index.html

And set the minimal instructions:

  layout: main_page
  title: All projects
    enabled: true
    category: projects
    per_page: 5

{% for post in paginator.posts %}
<h1><a href="{{ post.url }}">{{ post.title }}</a></h1>

  {% if post.description %}
      {{ post.description }}
  {% endif %}

<p class="author">
  <span class="date">{{ post.date | date: "%a, %b %d, %y"}}</span>

{% if post.main_image %}
  <img src="{{ post.main_image }}" >
{% endif %}

  Categories: {{ post.categories}}

    {% for tag in post.tags %}
      <li> {{ tag }} </li>
    {% endfor %}
{% endfor %}

<!-- Pagination links -->
<div class="pagination">
  {% if paginator.previous_page %}
    <a href="{{ paginator.previous_page_path }}" class="previous">
  {% else %}
    <span class="previous">Previous</span>
  {% endif %}

  <span class="page_number ">
    Page: {{ paginator.page }} of {{ paginator.total_pages }}
  {% if paginator.next_page %}
    <a href="{{ paginator.next_page_path }}" class="next">Next</a>
  {% else %}
    <span class="next ">Next</span>
  {% endif %}

In the yaml section, you can overide the _config.yml default parameters. I asked to select only the post within the project category. I could select all post also.

You can see that there are two blocks:

  • The first one give you an overview of the blog:
    • Title
    • Short description (in my yaml header)
    • An image
    • The list of the tags.
  • The second is about pagination. Here, let the magic happen.

NB: you need to restart jekyll, otherwise it won’t create the pagination

You can create multiple blog lister, filtering other categories.


I decided to build my own CLI for several reasons:

  • neocities GUI has some bugs (drop down nested folders and you will see the result).
  • neocities CLI is incomplete (no option for filtering stuff, missing function) (Link to the official CLI)

As the official CLI was written in Ruby, and I don’t want to learn about because of a lack of time and it is unlikely that I would use this language professionally, I decided to write a new one using python.

» Source Code on GitHub «

You have the documentation on GitHub.

It doesn’t require any particular libraries, and you don’t need to do any lib installation.

It is limited to Linux only (I use / and not \), but you can upgrade it to be platform agnostic.

The basic function I needed is to push one folder on a particular location.

When I create a post, I needed to update 3 things:

  • Add the post in its folder project/yyyy/mm/dd/my_project_x.html
  • Add the images in the image folder image/my_project_x/
  • Update the blog listing pages blg_proj/index.html and all the others

Adding the Post

To push the post, I have two options:

  • I can only add the single file.
  • I push all the post folder

As we have html pages (without the img), it is not too heavy, so we can add everything in one shot.

./neocities update project/ --rec, which would uplad everything to the root of the website.

If I want to add only this file, I need to set the path.

./neocities update project/yyyy/mm/dd/my_project_x.html --remote_path="project/yyyy/mm/dd/"

Last option is to only update this month (if you push multiple articles at a time).

./neocities update project/yyyy/mm/ --rec --remote_path="project/yyyy/mm/"

Adding the Images

This is not different from previous methods.

./neocities update IMG/*.jpg --remote_path="images/my_project_x/"

which is equivalent to

./neocities update IMG/ --rec --remote_path="images/my_project_x/"

I will endup with


Adding the blog lister

Locally, I have:


./neocities update blg_project/ --rec"

Would push this project to the root.

NB: this is very different from ./neocities update blg_project/* --rec", which would remove one layer and put all the content of blg_project/ to the root.

Other tools

There are others stuff that are helpfull.

Image Editing

I configured my phone to take high quality photograph, like 8000 x 6000 px which leads to $5 \sim 10$ Mb.

On the website, it is not convenient at all:

  • I often photograph object, where I don’t need a very high definition
  • Doesn’t fit in a regular screen.
  • Even if there are many pixel, all are not accurate, so you don’t loose quality when reducing the size by 2 or 4.
  • Pages need a lot of time to load.

The best option is to resize images to get a convenient size. My default size is 500 px width, so putting two photographs in one row is perfect.

For this, manually resizing all photographs would kill all your time. The best thing to do is to use a script to do it for you.

I use the comand ‘convert -resize ` which does the job.

Another point to consider is naming. My phone use typical naming like IMG_yyyy_mm_dd_hh_min_ss.jpg, so there is never ambiguity. For me, its not convenient at all, I cannot copy all these names.

There are two options:

  • automatic listing, using a python script or grep to print ![](pat)

>> You can subscribe to my mailing list here for a monthly update. <<