Hey there,
Today I tried to add Fractal to an existing platform with Jekyll, and ended up giving up for a number of reasons. This issue is not a feature request (well, would be nice but that’s up to you); it’s more like an inventory of things that should be done to make it possible to use Fractal with Jekyll.
To show you where I failed, I need to show you where I started. Let’s begin with my project structure. Note that for sake of clarity, I only show one component (button).
my-project/
|
|– _includes/
| |
| |– components/
| | |
| | |– button.html
| |
| |- head.html
|
|– _layouts/
| |
| |- default.html
|
|– assets/
| |
| |- …
|
|- config.yml
|- package.json
As you can see, that is a pretty standard Jekyll setup here. For the record, button.html looks like this:
{% assign type = include.type | default: "submit" %}
{% assign text = include.text %}
{% if text %}
<button class="c-button"
type="{{ type }}">{{ text }}</button>
{% endif %}
It would then be included like this:
{% include components/button.html
text = "Sign in"
%}
The first thing I’ve noticed is that you do not support Liquid as an engine. I first started with Nunjucks which is similar enough to Liquid in appearance, but it did not work. Unfortunately enough, Liquid is actually written in Ruby, so I could not even plug it in Consolidate.
I found tinyliquid though that is a lightweight port of Liquid in Node.js. I set it up through your Consolidate and it seemed to work okay.
const liquid = require('tinyliquid');
const consolidate = require('@frctl/consolidate');
const adapter = consolidate('liquid', liquid);
fractal.components.engine(adapter);
fractal.components.set('ext', '.html');
This was actually fine I believe.
After setting up a button.config.yml to pass some text to my button component, I was puzzled because it was not working.
# button.config.yml
context:
text: "Sign in"
Then I remembered that in Jekyll, Liquid parameters are being passed through an include object. I could successfully tweak my config file to make it work as expected:
# button.config.yml
context:
include:
text: "Sign in"
In Jekyll, most boilerplate stuff live inside underscore-prefixed (_*) folders, such as _includes, _data or _layouts. Apparently, Fractal omits these files and folders because it considers them as hidden even though I set _includes/components as my components folder in fractal.js.
fractal.components.set('path', __dirname + '/_includes/components');
The first thing I tried was forcing my component not to be hidden:
---
hidden: false
---
… unfortunately that did not help. The component can successfully by reached at http://localhost:3000/components/detail/button but it does not appear in the sidebar as you can see in the screenshot below.

The only solution to this problem seems to be changing the Jekyll configuration to use a folder that is not prefixed by an underscore which I would say is not ideal but at least that works.
my-project/
|
|– includes/
| |
| |– components/
| | |– button.html
| |
| |- head.html
|
|– _layouts/
| |
| |- default.html
|
|– assets/
| |
| |- …
|
|- config.yml
|- package.json
# _config.yml
includes_dir: ./includes/components
exclude:
- includes/
// fractal.js
fractal.components.set('path', __dirname + '/includes/components');

So far, the component can be rendered in the styleguide but it’s (unsurprisingly) unstyled (as you can see on the previous screenshots). This is when I wanted to render it with a layout. The first thing I found odd is not being able to pass a path to a layout or a layout folder, but I guess it has to do with the fact that you support multiple engines.
So I created a dummy layout in my components folder, prefixed by an underscore as I don’t want this one to be analyzed of course.
<!-- includes/components/_preview.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="/css/main.css" />
</head>
<body>
{{ content }}
</body>
</html>
// fractal.js
fractal.components.set('default.preview', '@preview');
It’s not ideal because it is basically a duplicate of my actual layout but that’s probably fine. Also we could probably apply the same fix as earlier and define the Jekyll layout folder to be the same as the includes one. Not great but probably working.
From there, two problems encountered that I really don’t know how to solve. The first one is that the path to the CSS file cannot resolved. I tried a few things (playing with relative paths) but I kept getting something like this in my console:
‼︎ 404: No matching route found for /css/main.css
The second one is that while the layout is correctly found and used, the component is actually not passed to the layout, so a blank screen is rendered.

This has to do with the fact that Jekyll uses this magic {{ content }} variable to determine where shall the content of a page be rendered in a layout file. Unfortunately, we are not in Jekyll (even though we use Jekyll) here and therefore we can’t seem to use a layout.
Because of that layout issue, I did not get that far but I suspect that nested components (a component that includes a component) would simply not work because {% include %} is not a Liquid tag but a Jekyll extension.
That’s where I stopped. Please feel free to tell me if I’ve done anything wrong or if you want some more information. I hope it helps anyway. ✨
@HugoGiraudel this is great information! Many thanks for the all detail. I can't go through it all right now as I'm about to head off but I will try and do so over the weekend and see if I can make any suggestions as to how to get the two playing along nicely. I'm sure you are not the only one who will want to do something like this!
Hi @HugoGiraudel - thanks again for the level of detail in your issue. Make things _much_ easier for me to respond to!
I think that most of these should be relatively straightforward to sort out. I'll go through them one by one here but please let me know if anything isn't clear or doesn't work for you:
Liquid as an engine - sounds like this is working fine through the consolidate adapter, which is good to know as I've not tested this one myself yet!
Liquid include parameters in Jekyll - It's definitely a little annoying that you have to have an additional include key in all of your context data definitions. This is something that could be addressed by writing a specific template adapter for tinyliquid, but without that there is not much that can be done I'm afraid.
Hidden folders - This is a bug... but one which I've (hopefully!) just fixed and released in v1.0.1. So if you npm update and then test it with your underscore-prefixed folders it should work. If not let me know! (Undescore-prefixed directories _within_ the component folder will still result in them being hidden, as that is the desired behaviour).
Layout template path - Regarding passing a path to a preview template (as opposed to specifying it a using a handle)... this _is_ actually currently possible. However currently the preview template must still live within the components directory. There is no real reason why it should be like this however, so in the next update I'll change this so that it's possible to link to any arbitrary template file.
{{ content }} placeholder - Fractal by default expects the placeholder within the layout to be called {{ yield }}, although you can change that with a configuration setting as follows:
fractal.components.set('yield', 'content');
I _think_ that fix the issue - if not let me know.
Asset file paths - If you want to link to static assets in a public directory or similar, you need to point the web UI server to that folder so it will serve them up. Docs are here: http://fractal.build/guide/web#static-assets but the config you need will look something like this:
fractal.web.set('static.path', 'public');
Then any files within the public directory will be served at paths relative to that directory - i.e. public/css/main.css would be served at the URL /css/main.css so you can then link to that in your preview layouts.
Nested components As far as I understand it, the consolidate adapter for tinyliquid should still let you use the include tag, but not in the Fractal-standard @handle syntax, only via file paths. However if you are consuming the same templates in your Jekyll installation that is probably what you want anyway as Jekyll would not understand that syntax anyway!
Hopefully the above will help to get it working for you - let me know either way, would be great to get it playing nicely with a Jekyll installation :-)
Hey Mark! Thank you so much for all the feedback. I’ll give it another try when I get a chance. :)
@HugoGiraudel @allmarkedup I'm interested in trying this too! This thread is super useful.
I’ll try soon and give my feedback on this post. :)
Hey,
I had a bit of time to play with this so let me get back to you with some extra information.
Liquid as an engine — solved with tinyliquid.
Liquid include parameters in Jekyll — solved with include key in YAML. Not super straightforward but works well. Might be worth putting in the docs?
Hidden folders — solved with 1.0.1, thanks.
Layout template path — solved by putting layout file in components folder. Looking forward to a better way to handle this as it seems weird to me.
{{ content }} placeholder — solved with configuration:
fractal.components.set('yield', 'content')
Asset file paths — solved with configuration:
fractal.web.set('static.path', __dirname + '/_site/')
Nested components — still problematic. Let’s say my Jekyll include folder is the default one: ./_includes/ and let’s say in there I have a components/ folder containing all my partials. Consider a form.html component using a button.html component.
When I include a file in a Liquid template through Jekyll, it would be done like this:
<!-- _includes/components/form.html -->
{% include components/button.html %}
However in Fractal, I get this error:
Error rendering component
ENOENT: no such file or directory, open '/Users/hgiraudel/Sites/jekyll-fractal/components/button.html '
Fractal uses a literal path (./components/button.html) while Jekyll uses its include path (./_includes/components/button.html).
Any idea how to solve this? My idea was to change the Jekyll include_dir configuration to ./ to match the one from Fractal. It’s not pretty but it seems to work.
And I found three extra problems. 💃
Jekyll-specific filters — again, Jekyll extends Liquid with some extra helpers such as filters. Because we are not going through Jekyll here but tinyliquid directly, custom filters do not work (in my case, markdownify, jsonify, uniq and {% increment %} block). See an example below:

Components README.md — When moving components to compound components as per the documentation, adding a README.md (with content, empty is fine) returns a full stack track. See an example below:

Iframe height when browsing components — when browsing components through the sidebar, the height of the rendering iframe of the current component is not correct. A page reload seems necessary to work correctly. See below:

File paths are not trimmed — I get an issue with include blocks using parameters on multiple lines, as the file path does not get trimmed properly. Consider the following example:
{% include _includes/components/hint-box/hint-box.html
title = title
copy = include.caption
%}
Fractal reports:
Error rendering component
ENOENT: no such file or directory, open '/Users/hgiraudel/Sites/cevt/_includes/components/hint-box/hint-box.html '
Notice the space after .html. Removing the line break between the path and the first parameter fixes the problem:
{% include _includes/components/hint-box/hint-box.html title = title
copy = include.caption
%}
Hi Hugo - so there are a few things going on here - some of which I can help with, some of which I don't think I can, and some of which _could_ be solved using a bespoke adapter (rather than using the consolidate one, that introduces it's own quirks!).
So the following sound like Fractal bugs:
fractal.js file here or as a separate gist for me to try and see if I get the same error here?These ones can definitely be solved (or improved) by writing a custom adapter for Jekyll/Liquid:
include key could be eliminated via the adapter.These ones however I don't think I can help with:
This has really piqued my interest and I'd really like to get this working, so I'm going to try and find some time to get together a quick custom Jekyll adapter as I think that is really the only way to get this working in a way that fits with both systems. I'll let you know when I've whipped something up :-)
I've also found this: https://github.com/sirlantis/liquid-node which I may investigate as an alternative to tinyliquid if it starts causing any issues.
Hey Mark. Thanks for all the feedback, I’ll get back on this at some point. :)
Components README.md — Here is my fractal.js file. The content of README does not matter as long as it contains at least a character. Empty works fine, any character triggers the error.
Jekyll-specific filters — I have been investigating further on the Liquid side. liquid-node is unfortunately not an option as it does not expose a newContext method, apparently needed by consolidate for the Liquid templating engine.
On the other hand tinyliquid does expose this method, so there’s that. Also, tinyliquid is the fallback engine for Liquid in Consolidate, so something tells me that’s the good way to go.
Now, we need to be able to extend tinyliquid with custom tags and filters to make it synced with Jekyll in that ground. After reading its wiki, it seems to be possible to do that by passing options when creating a context. However, the context is created by consolidate, so this is not as straight forward.
Fortunately, consolidate still makes it possible to extend tinyliquid by passing options to its render method, which are then applied to the context with .setFilter().
Unfortunately, the render function is called by @frctl/consolidate. It seems possible to pass a context to the render method of @frctl/consolidate but I have no idea how to do that.
Edit: I went further. The render method from the engine is called by the render method of the ComponentSource class. This one is called by the renderPreview method of the same class which seems to pull the context from the config file, I guess…? But I don’t see ho I can pass functions in a YAML file.
Do you have any idea @allmarkedup? :)
File paths are not trimmed — I submitted a PR to tinyliquid: https://github.com/leizongmin/tinyliquid/pull/49.
Yet another problem, Jekyll uses the magic include variable when including a file with parameters. We can work around this by passing an include key as well in the config as we discussed already.
However, it does not work for nested components. As they are being rendered by tinyliquid and not Jekyll, the include variable does not exist in the nested included component. Maybe once we find a way to include tinyliquid, we can tell it to do the same to be in sync with the liquid rendered from Jekyll.
No problems... just 'challenges' ;-)
I have to dash off now but I've just put up the start of a custom adapter here: https://github.com/allmarkedup/frekyll - I think removing consolidate from the equation is the first step in getting a working setup.
I'll try and get a bit further with it tomorrow evening, at least far enough to see if it's actually feasible/useful.
Very good. Let me know how I can assist you in the mean time. Feel free to get in touch here or on Twitter (@HugoGiraudel).
Hey @HugoGiraudel just to let you know that I haven't completely forgotten about this but I'm currently drowning in stuff that sadly has to take priority (i.e. bugs and other issues that are affecting more people).
I'll get back onto it as soon as I can (and when I'm back from holiday!).
Off-topic - Your blog and articles are awesome. I've had to dive pretty deep into some Sass stuff recently and a lot of your writing has helped me a lot, so thanks 👍
Hey, thank you so much for the kind words. No rush @allmarkedup, feel free to ask if I can do anything. :)
While this thread is a year old, I'm curious if any progress was made! Jut trying to get the two to play nicely together and stumbled on this before going too far down the rabbit hole
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
Most helpful comment
While this thread is a year old, I'm curious if any progress was made! Jut trying to get the two to play nicely together and stumbled on this before going too far down the rabbit hole