How to go around measuring text synchronously?



Summary: I’m using elm-collage to display some text. This package used to make a native synchronous call to measure the text size. Of course, that isn’t possible anymore. So, they’ve “reverted to a very simplistic calculation using the font size and the string length”, which of course causes text overflow with non-monospaced fonts.

I understand there’s background for not allowing synchronous calls and everything. I’m also okay with wiring ports manually. My problem is that I can’t think of an easy solution, whether as a package user or author, even if I’ll need to wire some ports.


  1. As a package user (not author), how would you go around this issue? Do you have any solution in mind?
  2. If you were the package author as well, do you have any alternative design with good UX even if it’ll require wiring ports?

Notes that might be helpful:

  1. There’s an svgBox function that allows me to render in a given width and height.
  2. You can assume I have in Elm the knowledge of the text’s typeface, size, bold or not, … etc.

Thanks in advance!


For computing the text width, there is this package from @folkertdev:


Yes. The package a work in progress, but should make this possible.

A problem if you want it to be synchronous is that currently, I still need to load the font file to parse it and extract the size of each character. The idea is to have some web-app somewhere that you can give a font and it will spit out a nice record that can be used to calculate the size of a string*. Possibly a package containing that data for some commonly-used fonts can cover most use-cases.

* For simple strings, will probably fail on emoji, or non-western scripts. But it should cover most web use cases, and we can see about the other cases when need arises.


Good point, I did not give this much thought before. If the font file is only loaded at runtime, the overall process would still involve a non-synchronous operation, happening when the font becomes known.
But if you can determine the font(s) at compile time, you could add the font file(s) into the Elm app. Then the solution would be completely synchronous?

In cases where the implementation of the browser should handle the actual rendering completely (not taking over from SVG) I guess the font file could be made available with File.toUrl?


I ended up using ports for this. I have the advantage of using only static strings that can be measured once (although the app also scales). I also use CSS animation to hide the initial state where things are not yet correctly aligned.

Not a real solution, but thought what others are doing might be useful context for the problem in general.


@folkertdev Thank you, will keep an eye on the package to see how the experience would turn out!

@wondible That’s a good idea! Actually, my use-case is indeed static!

Does your code happen to be open-source in order for me to not reinvent the wheel?

(P.S: Thank you Viir for pointing out to elm-cff! )

  • JS in index.html
  • src/MeasureText.elm
  • searching for MeasureText in src/ScheduleFromVideos.elm should get you to the uses.
  • css is inline in src/View.elm

Live Example:


If your use-case is static, maybe you can just store the widths of the characters in a mapping function and do something like a string |> toCharlist |> charLength |> List.sum?


@wondible Thank you; that’s beyond awesome! … I’m also thinking about precomputing in Javascript the widths of all the strings used in my application and then giving everything in as flags, in order to not have any asynchronous code in Elm.

However, haven’t actually tried it, so don’t know if that would affect first-render time or if there’re any other implications.

@yawaramin That’s a good idea, but for some reason I have an internalized knowledge that the width of a string isn’t necessarily the sum of widths of its characters. I can trace back this knowledge to two points:

  1. What about the margins between characters?
  2. The more important point: ligatures.

Anyway, not an expert here.


In case you are interested, I have a ports solution for this:


This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.