Single-page app: how to reference assets with --no-html

Hey ya’ll! I’m building a single-page app backed by a Phoenix API. I don’t think I need phoenix_html so I did mix phoenix.new --no-html, but now I’m not sure where to put the single index.html.eex page which references my CSS and JS.

  • I don’t think I can put it under web/static/assets because it’s not a static file. I think I need EEx and <%= static_path() %> (which comes from the router, not phoenix_html) to reference my CSS and JS, which have dynamic filenames like /js/app-2bb77dbddd1cfc28bc67e5ed3fbdd289.js
  • If I put it under web/templates/page I get an error: (RuntimeError) Could not load Phoenix.HTML.Engine to use with .html.eex templates.

That error makes it sound like all .eex templates require the phoenix_html dependency, even if I’m not using any of its HTML helpers.

What am I missing? Again, I’ve got a JSON API that just needs a mostly-static HTML page to reference my CSS and JS. Thanks for any help!

2 Likes

IMO, the quickest solution here is to just add phoenix_html back to your app (or create a new phoenix project, this time without the --no-html flag). You might need it anyway later for something you don’t foresee. It’s an extra deps, but saves you from the trouble.

That said, you actually can call static_path('js/app.js') from your router (as long as you keep the import MyApp.Router.Helpers line on web/web.ex), load the index.html file as a string, and somehow inject the result of static_path to the string (through String.replace), and use html conn, html_string to send the resulting string back to the client.

However, I think this approach circumvents the blazingly fast template caching that Phoenix has, though. I have no idea how it would affect the performance.

If I want to build a SPA, I usually built the client as a separate project, say as a Node.js front-end app with React and Webpack. Kind of a hassle, but there’s a lot of boilerplate templates that I can use and it’s not tied with the server’s stack. I haven’t build SPAs with Phoenix though, and if I do I might just go with the phoenix_html approach to simplify things.

3 Likes

You can’t have an html file without phoenix_html unless you do something sort of hacky like Bobby said above.

--no-html is when you want JSON only. You should either put it back or have your own repository/project for the single page app. I personally thing this is a good approach because every time I see someone put a complex JavaScript app inside their backend it becomes just a mess. Maybe I just really like a clean separation of concerns in projects though.

3 Likes

Thanks for clarifying things :thumbsup: Of my 3 options:

  1. Bring back phoenix_html
  2. Render a raw HTML string in the controller with html(conn, str) using static_path there
  3. Build a separate front-end app

I’ll probably go with 1 for now but I am interested in benchmarking 2. phoenix_html isn’t a large library, but I would rather not pay the memory overhead of including it, so long as it’s not offset by losing the efficiency of Phoenix’s template caching. Nathan Long has a great post on how Phoenix uses IOdata to make templates so fast, so perhaps I can take advantage of a similar approach. Thanks for suggesting and explaining that @bobbypriambodo.

As this is a one-person side project where I’m often adding JSON endpoints and corresponding React components at the same time, separate repos with 3 probably isn’t worth the additional complexity, though I agree, @sotojuan, that that would be the ideal, cleanest solution.

Thanks for the helpful responses!

2 Likes

There really is not much overhead there. :slight_smile:

3 Likes

I support the idea of a separate repo and then just tossing your bundles in an S3 bucket. If you are using react but don’t want webpack headaches then create-react-app is the fastest way i’m aware of to get started. I did read the previous responses, so i’m aware of your benchmarking goals. Going with option one seems reasonable. I hope you find a setup that suites you best.

2 Likes