responsive images inside a StructuredText

Posted by on Aug 04, 2014 Comments Improve this article

Once again, I will not explain what is (but it’s a tool to manage your website content), I will only focus on one limitation and how to solve it. If you don’t know about, you should quickly check its website before reading any further.

What’s the problem?

In, you are creating Documents which are composed of Fragments. A fragment is data organized in a more or less semantic way. For example, a fragment can be a Number, or a Date, … Among them, there is the type Image which is really nice because you can not only fully resize / crop your raw picture, but also add smaller images based on the original one for responsive purpose. You can upload your awesome photo of this beautiful sunset, fully sized at 4000 x 3000 pixels, resize it for desktop at 1920 x 1080, and finally add a mobile version at 320 * 480. Note that the last one doesn’t respect the ratio at all, but that’s the goal: you are creating a subset of your image with the best match possible regarding the targeted screen.

Another really powerful fragment in prismic is the StructuredText. It allows you to have a nice WYSIWYG editor inside which you will put new fragments (paragraphs, links, titles, images, …). It would be perfect if the image fragment inside a StructuredText was a real Image fragment, but it is not. You cannot specify any thumbnail. Meaning your content cannot be responsive at all. Your choice is: consume all the bandwith for mobiles with awesome full HD images or make the eyes of your desktop users bleed with super compressed images.

You don’t want to do that!

And me neither! I want to have responsive images inside my StructuredText for God sake. I love tools which make my life easier, but they never should stand on my way to craft the best website possible. I will not lie to you, I couldn’t find any solution that integrates nicely with the WYSIWYG editor since there is no way to extend it. The following solution will be a bit of hacking and your writers might complain about it at first.

So, what’s the idea? The only way to have responsive images is to use a real Image fragment, no choice here. So let’s create one, and let’s put it inside a Group fragment. This way, you can add and remove as many images as you want and they will all be responsive. Wait, you can’t do that inside the StructuredText, right? Indeed, that’s why we will do outside of it and then find a way to bring back the images inside of it. Here is the mask for such a group:

  "Images" : {
    "images" : {
      "type" : "Group",
      "fieldset" : "Images",
      "config" : {
        "fields" : {
          "name" : {
            "type" : "Text",
            "config" : {
              "label" : "Name"
          "caption" : {
            "type" : "Text",
            "config" : {
              "label" : "Caption"
          "image" : {
            "type" : "Image",
            "config" : {
              "thumbnails" : [ {
                "name" : "mobile"
              }, {
                "name" : "tablet"
              } ]

Pro Tip - When creating thumbnails for an Image fragment, the offical documentation states that both width and height are mandatory. This is wrong, you can specify only one of the two or even none of them. Your UI might be a bit ugly (the resized thumbnails will only display after saving your draft) and sometimes you will have to click twice on a button (this one, I really don’t understand) but otherwise, it works just fine. I hope prismic guys will make those attributes optional because… well… how can you anticipate how the writer wants to resize its image?

As you can see, I added two more fields. One is name and we will use it to reference our images inside the StructuredText. The other one is caption, that’s because I want to put one on my images but prismic didn’t allow me to do that at all by default. Thanks God, using this solution, I can finally do it, killing two birds with one stone. Now we can add as many responsive images as we want, yeah! Let’s grab some orange juice to celebrate!

Let’s hack some HTML

As I already said it, the next step will be to bring those images inside the StructuredText. We cannot do that directly inside the WYSIWYG editor but we can do it when rendering the final HTML. We will need two more things here:

  • having a placeholder inside the StructuredText to indicate we should insert an image
  • extend the default HTML renderer to support such placeholder

As for the placeholder, I decided to use a simple format like {image-[name of the image]}. Meaning that if the content of a paragraph inside my StructuredText is {image-sunset}, it will actually render the image named sunset from the group of images when generating the final HTML. Pretty easy right? Your writers might find it ugly, having to write those strange tags, and not seeing their images directly inside the StructuredText, but just tell them that’s for the greater good. You could, of course, use another syntax, eventually support attributes, whatever.

The final task, and probably the hardest one, is to extend the HTML rendering system. Each primisc fragment has its own way to render as HTML. Unfortunately, there is no way to extend it, you can only, eventually, override it. And the one for StructuredText is by far the most complex one. The easiest way to do that is to copy/paste the default one from the kit you are using (if you are not using any kit, you are free to do whatever you want, so it’s fine) and edit it. You will then edit the part responsible for rendering a paragraph, test if its content match our placeholder syntax, if so, render an image, if not, just render the text as it is. Since I’m using the JavaScript kit, here is the full copy/paste but that’s the important part where we are rendering the image. My model is actually a JavaScript class extracted from the original prismic Document, but you could use the raw Document of course.

Conclusion and limitations

That’s pretty much it. Here is a quick summary of what to do:

  • inside the mask, add a Group of Images with, at least, a Text fragment for the name.
  • choose a naming convention to write placeholders inside a StructuredText, ex: {image-[name]}
  • override the StructuredText HTML rendering function with a custom one inserting images from the Document when finding a placeholder
  • you can add more attributes and render them (like a caption)

What are the bad points / limitations of the solution?

  • writers need to write placeholders rather than having a nice UI to insert images
  • images do not display natively in the WYSIWYG editor anymore
  • you cannot re-order a Group fragment right now, meaning images will be sorted based on their created date and not on their place inside the StructuredText JavaScript kit: from callbacks to promises

Posted by on Jul 26, 2014 Comments Improve this article

Holy cow, callbacks!

For the last (and only) website I did using, I didn’t want any back-end, all client-side, pure JavaScript man. This way, I could host it nearly anywhere for pretty much free (GitHub pages FTW). To ease stuff, I decided to use the JavaScript kit from the team.

One choice they made and that I don’t really like is using callbacks in it. For example, you have to write stuff like:

// Get the current API
Prismic.Api('/my/awesome/url', function (err, api) {
  if (err) {
    // handle error

  // Use the API there, retrieving documents
  api.form('articles').ref(api.master()).submit(function (err, articles) {
    if (err) {
      // Also error again

    // But I also need authors...
    api.form('authors').ref(api.master()).submit(function (err, authors) {
      if (err) {
        // One more time, handle error

      // Now I got all my data
      // I can finally display it on my page
}, 'my_token_far_below_at_the_end');

Can you see it? This ugly thing JavaScript experts name “callback hell”? And that’s a super simple example. In 2014, the way to do that is using Promises. That’s what’s hype (and readable, and nice, and cool, and swag). But I quite understand why they made this choice. Promises are not native everywhere yet, so going with it mean either writing your own implementation or imposing an existing one. Fair enough to have callbacks in the kit then, but we are hackers here, we can override it to use Promises!

I will use the last spec Promise syntax, native way baby!, but it is really important to keep in mind that it has a really poor support right now. You should probably be using a Javascript library as Promise implementation. I will link a snippet of code using the Q library at the end of the article. But I wanted this article to be future proof.

OMG, promises \o/

There are two main callbacks in the kit: when retrieving the API and when making a submit. What I want to write is something like:

Api('/my/awesome/url', 'my_token_right_here_at_the_start')
  .then(function (api) {
    return Promise.all([
  .then(function (data) {
    var articles = data[0];
    var authors = data[1];
    // Display the results
  .catch(function (error) {
    // Handle error

Sooooo much more readable and swag… Overriding the Api callback is really simple:

// Remove the callback from the function signature
function Api(url, accessToken, maybeRequestHandler, maybeApiCache) {
  return new Promise(function (resolve, reject) {
    // Put a callback that will only resolve/reject the deferred depending
    // on the presence of an error or not
    Prismic.Api(url, function (err, resolvedApi) {
      if (err) {
      } else {
        // Save point 1 (see below)
    }, accessToken, maybeRequestHandler, maybeApiCache);

Overriding the submit call is a bit more tricky because it is a prototype function of the SearchForm object, which is totally hidden inside Prismic clojure. This is a bit (super) sad but we can deal with it by creating a fake form and not submitting it. The easiest way I found so far is doing it right after getting the Api. Let’s add the following code at Save point 1 (see previous code).

// You might need a polyfill for 'getPrototypeOf'
// if you support old browser
var SearchFormProto = Object.getPrototypeOf(resolvedApi.forms('everything'));
// We are saving the original function so we can call it inside our override
var originalSubmit = SearchFormProto.submit;

// Let's override!
SearchFormProto.submit = function () {
  return new Promise(function (resolve, reject) {
    // As before, the callback will only resolve / reject the deferred
    // depending on the returned value, function (err, docs) {
      if (err) {
      } else {

And after that, you just need to use the new Api function in place of Prismic.Api. You can see the full code here using Q library. This refers to a particular commit to ensure the line highlight is correct but you can freely switch to the master branch to see the most recent code (which shouldn’t be much different… but who knows…).

Thanks for reading. Next time we will talk about responsive images inside StructuredText. when happiness met disappointment

Posted by on Jul 17, 2014 Comments Improve this article

Warning This article is pretty old. Prismic has evolved a lot since then and most issues have been addressed. Check the last version of the docs.


If you don’t know about, it’s a nice tool providing both a super clean web interface to write content and an API to retrieve it. No front-end provided, eventually some kits to help you and some examples, but no more. Which is cool because, if you are a web developer, you can focus on crafting an awesome optimized website and then easily put some content in it… well, that’s the theory…

But for me, after using it for a new website, as good as the idea can be, I have found way too much limitations to consider it production ready. The team behind is working on most of them so I really hope the implementation will become as awesome as its concept at some point in the future.

The rest of the article will focus on bashing all those limitations, so there will be way more bad than good, but don’t get me wrong, has tons of good stuff, and it’s still in beta, I just want to talk about what I don’t like in this article. And since I’m not all about trolling, I will try to consider how to solve them at the end. I will not explain what is exactly, so it might be for the best that you quickly check their website before continuing reading. Enjoy.

Do you remember how happy we were at the beginning?

I will never forget… it all started on this windy saturday… I was looking for a solution to create a new blog about traveling. I didn’t want to use an all-in-one tool because I love crafting my UI from scratch, with exactly what I want on it (this point is super important for the rest of the story). I said bybye to Wordpress and all its friends. If I were alone, I would have probably go with raw Markdown or whatever, but I am not… I have other people that travel with me and want to write articles without learning a super complex geek syntax. I will call them… writers. Some colleagues of mine told me about you… that you might be the future of content management, that I would be free for the UI, that you had WISIWIG editors and stuff for… writers, sounds like a perfect match, and so our adventure began.

I still can’t believe how fast it all went… in a couple of hours, we were able to write new articles and display them in a custom website hosted in GitHub pages. Using one of your kits, it was so easy to query your API, retrieve the content as JSON and inject it inside Handlebars templates in order to render the final HTML. Oh man… it was so good.

And then fictions appeared

A few days later, I wanted to go further, moving forward to more crazy stuff. I know that sometimes I try to do complex features just for the fun of the challenge it provides me, but still, how could you do that to me… Remember this conversation?

  • Hey, I would like to query documents that does not match this predicate
  • Ahahah, you so funny, no way…
  • Thanks! Wait… what? Why not?
  • I don’t have a NO operator or whatever close to it.
  • Whyyyyyy? You have crazy operators like similar to retrieve similar documents based on God know which algorithm and you cannot do a simple negation?
  • Nope. Deal with it.
  • Okay… I think I can manage something if the query match this OR that. That’s fine, right?
  • No.
  • Cool! Wait… what?
  • I only have AND operators between predicates.
  • You gotta be kidding me…

I was a bit sad, but I could manage it, retrieving too much documents and filtering client-side. Still, that was the starting point were all went wrong.

My biggest mistake: using StructuredText

All contents in must be typed. This is an image, this is a number, etc… A StructuredText is probably the most powerful type since it allows writers to enter complex content through a WISIWIG editor, using paragraphes, images, lists, embedded stuff, … They can style it using bold, italic, and so on. It looks nice, right? So I decided to use it for the main content of my articles. Fair enough. As always, it was so great to have writers doing their own design using the editor, and so easy to render it automatically as HTML (except for some bugs here and there, but that’s what pull requests are for, isn’t it?). At some point, I just wanted to have a lightbox on some images: clicking on them should expand it to its full sized version with a caption. I don’t know for you, but for me, this is a really basic requirement and you should be able to do it in less than 2 hours.

First problem: I wanted to tag images that should have lightbox with [data-lightbox] HTML attribute, all images didn’t need one. Since I was using the JavaScript kit to render my HTML, I couldn’t do that at all. There is no way to add custom rendering or plugins in the HTML renderer from the kit. Dammit. So I rewrote the full renderer so I could customize it… Wait, how can I indicate which image has a lightbox and which hasn’t inside a StructuredText? Oh, right, I just can’t. Inside a StructuredText, you can only drop raw images and resize them, that’s it. Does that mean I cannot put nor caption or title? Yep, no way to do it. So I started to hack: if after an image, the following paragraph would begin with [caption], the image would be a lightbox, the content of the paragraph would be its legend, and the paragraph itself would be removed from the final rendering.

Guess what? Writers didn’t really like having to manually write [caption], they wanted a nice UI to do that… but that was just impossible… They throw some tomatoes at me and we tried to move on but we couldn’t… Solving one problem to discover a new one: when you define a type image in a Document, you can tell that it will have different sizes: one for desktop, one for tablet and one for smartphone for example. And that’s awesome because you can fully optimize for mobile, which is super important nowadays. Do you think you can do that with image inside StructuredText? Indeed, the answer is no! Once again, a great idea not fully supported. Is there a workaround? Sadly, no easy one. You can upload the same image several times, one for each size, then find a way to retrieve its generated ID by and use the same hack as [caption] to specify it and hack a bit more the HTML renderer. I couldn’t find the courage to do it… pretty sure tomatoes would have been replaced by rocks from writers. I can only hope my mobile users have 4G now.

Want more? No biggy, I have tons of stories like that. A writer wanted to have blockquotes: a whole paragraph should be displayed in a custom design and have an author. I had to kill him really fast and bury his body deep. Another one wanted semantic distinction between paragraphs, something like: this one should be red and this one blue just because. Thrown him into a bucket full of piranhas. At some point, a writer became a madman and deleted half of documents… no way to find him since the delete operation does not appear in the timeline. But who cares? I mean, the document are gone anyway. I18N, do you speak it? I hope not because there is no support for it… yet. Want to put some tables? Could you please stop joking, it hurts me to laugh so much. Want do manage hundred of images in the media library? Might be less painful to jump from the top of the Eiffel tower. And so on, and so on…

Writers are generally really proud of their work, so they want everybody to know about it. I couldn’t just display “Title of the article”. If I wanted to stay alive, I needed to display “Title of the article, by an awesome author”, and if you would click on the name, you would access to a page saying how incredible the author was. As always, I was like “No biggy dudes, this is gonna be easy”. I wasn’t bluffing at that point since there is this nice feature that allow you to link documents between them. So every author would now have a dedicated document of type Author, where he could write whatever description he wanted, and each document of type Article would be linked to an Author. So far, so good.

And then the problem: if I get a list of Articles, I only got ids and a link to the Author resources, but I do not actually have the data on the Author document, which means I cannot display the real name of the author. And guess what? There is no aggregation or join possible in the API. You just cannot say that you want the 20 most recent articles with their authors. Solutions are: request all authors and then retrieve the good ones from ids (that’s 2 requests, and I did that since I don’t have that many authors), or do N+1 request (1 for articles, N for authors, one per author id from articles), or generate dynamically a request for authors using any operator (something like [:d = any(, [id1, id2, ...])]). In any case, you need multiple requests, eventually not running in parallel, and with too much payload… mobile first, right?

I never though you would mask it from me…

One core concept in is Document Mask. Each document must have one and it represents its pattern, how it will be persisted in database and how it will be served by the API. So you start creating masks, easy. It directly provides a slick UI for writers to enter content. Nice! And at some point, you realize you could have done better, the mask can be improved. Just don’t do that, keep going with your broken mask, trust me!

Let’s say you don’t trust me (that’s fair enough, you don’t even really know me). If you rename any property of the mask, you are screwed. The new property will be empty since it doesn’t consider it a renaming but the suppression of the previous property and the creation of a new empty one. But you are super courageous, so you migrate all your documents to the new property, yeah! Now you have duplicate a property in your API. Hell yeah! Just check the raw JSON, you will see the new property (thanks God) but also the old one. Properties just keep adding, never forget… never! Your payload will just increase. And be sure that your front-end developers always only rely on the mask definition, never on the data from the API, because… it doesn’t mean anything anymore.

My heart was so fragmented…

So, let’s summarize what’s the biggest problem with you can only do what the tool allow you to do. And right now, it allows you only super basic features. Which are needed obviously, but when you go to the real world, you know, outside of the matrix, those are just not enough. Imagine being Mario, you need to save the princess but you can only move forward, no jump, no strange mushroom, no yoshi… Good luck! Direct consequence, the killer feature that would make really powerful, in my humble opinion, is what I will call “custom fragments”. The idea is to allow developers to create their own type of fragments and plug them both in the UI of the writing room and in the HTML renderer. That might sounds a bit challenging but it isn’t that hard in fact.

So first, how to create a custom fragment? Let’s use the mask system. When defining a document, you are creating a mask saying “this document will have an image and some text as title”. Why not doing the same for custom fragments? Remember the lightbox problem? I would create a custom fragment lightbox which is composed of an image and some text as its caption. Even better, since I am now using a real image type, I can specify several sizes for the image. Big win here.

Next, inside the writing room, how to render a custom fragments? Well, you already know how to render all its pieces, just group them together with an indentation or a left border or whatever. The lightbox fragment would be an image selector and a text input. Of course, you should be able to put a custom fragment both inside a document (like any other fragment) and inside a StructuredText if possible (just like you are allowing em or h2 inside a StructuredText)

// Let's define our custom fragment for lightbox,
// just the same syntax as a Document Mask
  "title" : {
    "type" : "Text",
    "fieldset" : "Title",
    "config" : {
      "placeholder" : "A short caption for the image"
  "image": {
    "fieldset" : "Image",
    "type" : "Image",
    "config" : {
      "thumbnails" : [ {
        "name" : "Icon",
        "width" : 100,
        "height" : 100
      }, {
        "name" : "Column",
        "width" : 320,
        "height" : 320
      }, {
        "name" : "Wide",
        "width" : 600,
        "height" : 300
      } ]

Finally, each kit should have a renderFragment(type, json => html) method that allow you, for a specific fragment type (here lightbox), to pass a function that know how to render the final HTML from its raw JSON.

// Define how to render your custom fragment
// from JSON to HTML (which is a String)
kit.renderFragment('lightbox', function (json) {
  var html = '<img src="';
  html += json.image.url;
  html += '" data-lightbox title="';
  html += json.title;
  html += '">';
  return html;

Hey, we can do even better, let’s add a second argument to the rendering function, this one will be the original rendering function for “native” fragments in This way, you can override just one or two “native” fragments. For example, if you want to render image with a <picture> HTML tag because that’s super hype. Just do something like:

// Override an already existing fragment
kit.renderFragment('image', function (json, original) {
  var html = '';
  html += '<picture>';
  // Keep it easy, only one source...
  html += '<source src="' + json.image.url + '">'
  // Fallback using the default renderer
  html += original(json);
  // And the alt text
  html += '<p>' + json.image.alt + '</p>'
  html += '</picture>';
  return html;

And that’s it! You just solved so many limitations…

I didn’t understand your semantic anymore…

Ok, next step. Inside a StructuredText, I need to have semantic distinction between fields of the same type. This paragraph is important, this one is a blockquote and all the rest are normal. Let’s go the easy way: I just need a hash of key -> value, as strings, to reach a limitless world. This way, I could tag one paragraphe important: true, and another with with both blockquote: true and author: Someone. At last but not least, expose it in the API:

// Since all fields have a common structure to expose their type
// it's easy to have a new key like 'attributes' at this level
// to expose the semantic hash
// (btw, it would be cool to map to primitives rather than only string for values)
  "type": "paragraph",
  "text": "bla bla bla...",
  "spans": [],
  "attributes": {
    "blockquote": true,
    "author": "Someone"

I have no idea how to integrate that in the UI of the writing room, but I know its designer, he will figure out something awesome, no doubt.

By adding that, you leverage tons of possibilities for developers to customize the rendering of a StructuredText without any heavy hack. Writers would still need to manually write each key name, but its way better than paragraphs starting with [caption] or [author]. Following a snippet on how to render a blockquote, using the previous renderFragment method of course:

kit.renderFragment('paragraph', function (json, original) {
  if (json.attributes.blockquote) {
    var html = '<blockquote>';
    // Previously private (at least in the JavaScript kit),
    // I hope this method will be available in all kits
    // it applies all span styles to the raw text
    html += kit.insertSpans(json.text, json.spans);
    html += '<footer>';
    html +=;
    html += '</footer>';
    html += '</blockquote>';
    return html;
  } else {
    return original(json);

This feature is for “quick and dirty” hacks if you could say, and allowing you to customize “native” fragments. Because otherwise, you could also have done that with a custom fragment. Rather than having two attributes, a blockquote could be a custom fragment grouping a StructuredText for the content and a simple text for the author. It is way more intuitive in the UI and it’s easier to create its own renderFragment.

I like a lot this renderFragment method, but I think we can make it even better! Right know, we can only specify / override for all the application. That’s super cool but limited… and if you have read carefully until now, you should know that I don’t like limitations. This method should be at least at 3 different levels:

  • Document instance
  • Document type
  • (Collection?)
  • Application

Here is the idea: when an instance of a Document wants to render some fields (or fragments) as HTML, it will do the following simple procedure:

  • Hey, do I have a custom renderer method for this type of field on myself? (yeah, because now each Document instance have the method). If so, use it, if not, continue.
  • I am a Document of type Article, does this type of Document has such a method on it? (yeah, because now you can call this method for just a particular type of Document). Yes, use, no continue.
  • Eventually, does my Collection have the method?
  • Nearly finished, does the method is defined at the application level?
  • If nothing found so far, if it is a native field, render it as it should be, otherwise just crash or return empty string, I don’t know, it shouldn’t happen anyway.

Holy crap… we just created a “rendering context with awesomeness inheritance” or something like that. Can you imagine all the things you could do with that? Can you ?! I can’t… it’s too big…

Let’s break up

I could say more, but since it’s a miracle that you are still reading and it would not be as relevant as what I already said, I will stop here.

My conclusion so far: is not ready for the real world and for real websites but there is a lot of work going on by its team so, as they say on half of their responses: stay tuned! It might become super awesome at some point (I hope it will).

Thanks for reading! See you soon for more hacky coding posts.