Remake the Web.
This is a story of two problems and two solutions.
My first problem was a problem of my own creation. I read a lot of blog posts online, many of them so good that I save them for later, to re-read and share. I started with a simple page on my website where I just kept a list of those links, and why I enjoyed them. But eventually, the list became slow and annoying to update manually -- I wanted an app.
That’s when I came upon my second problem, which was very much not my own: Even for something super, super simple, like I want a page on the Internet where I can share an updated list of links, it takes a lot of work to get something working. The road from idea to prototype is covered with excuses not to build, and that problem is bigger than myself.
Remake sweeps away those excuses. I took a few hours on a Friday to sketch out an idea for Shelf.page and build it with Remake. You can check out my personal link stash on my Shelf.page.
Shelf.page is representative of a really common kind of app. Every user gets a profile or account page at a unique URL, with a bunch of fields to customize their page and edit it themselves. Remake shines at building these kinds of apps.
The rest of this post is about Remake, and how it could cut down your time-to-prototype to help you build faster.
If you already know what Remake is, and want to just get started with code, feel free to skip down to the Create Remake section.
Remake lets you write an app just by describing your idea in templates, not writing boilerplate code.
When you get down to it, Remake is a framework. And frameworks are always about abstracting out some repetitive, common part of projects. So a good way to understand Remake is to ask, what does Remake do for you?
In my experience, Remake is built on three big ideas.
The headline feature of Remake is that you can describe your entire app in a few templates. Remake looks at the shape of your data, and some annotations you add to your template, and takes care of making data editable and interactive in your page.
We’ll dive deeper into Remake’s annotations (attributes) later in the post, but here’s a taste. Let’s say you want every user to have an editable “name” field on their account. You can write a template with some attributes, like
<div class=”user-name”
data-o-type=”object”
data-l-key-name
data-i-editable>
{{ name }}
</div>
This tells Remake that
- This field is a property on the user’s account object (
data-o-type=”object”
) - This field should be stored under the label “name” in the user’s account (
data-l-key-name
) - This field should be editable on the page, for the account owner (
data-i-editable
)
There are a few more directives that combine together to add a layer of editability over your app that’s smart enough to know how you store your data. Combined with Handlebars templates, Remake gives you a way to create fully interactive, editable web pages with just templates sprinkled with annotations.
Write templates that describe how your data relate to your UI, and Remake does the rest. This is a core idea of Remake.
I’ve made lots of side projects over the years, but every time I start a new one, I always need to stop and ask myself if I want to support user accounts, because authentication and account management is an evergreen hassle. Even for prototype and quick hacks, setting up a database, creating login flows, copy-pasting code from some other project to remember how to implement login securely … all of this takes time, and none of it adds to the actual functionality of the idea I’m trying to bring to life.
Remake apps know how to set up and manage accounts, so in building my own Shelf.page app, I never had to worry about setting up an authentication system. Because I also deployed through Remake’s CLI and platform, user accounts just worked -- password resets, login, account pages, the whole thing.
There’s a tradeoff to this zero-configuration setup: There’s not a whole lot you can customize about the login process. It doesn’t support logging in with Google or Apple accounts, for example, and doesn’t support two-factor authentication. But until your idea grows out of the “prototype” phase, having to worry little about account management will probably save me hours off each project, and add years to my life.
A lot of the strengths of Remake like zero-config accounts, smart templates, and easy deploys are possible because Remake is opinionated about how your project should be set up.
Like Ruby on Rails or Create React App, Remake comes with a CLI that helps you set up a Remake project. If you follow Remake’s conventions about where files go and how pages are rendered, in return Remake’s CLI also gives you a local development server out of the box and a way to deploy to production to Remake’s deployment service with a CLI one-liner, remake deploy
.
A second benefit of an opinionated design is that, if you have multiple Remake projects, you’ll never have to open up an old project and sit there trying to remind yourself where you put the exact file or page template you’re looking for -- every project is roughly structured the same.
Taken altogether, by having a clear, opinionated model of describing how your data is related to your UI, Remake lets you write an app by just describing your idea, not wiring boilerplate code.
Let’s see how these pillars of Remake come together to actually help build a real app.
Every Remake project starts the same:
npx remake create shelf
npx is a package runner for NPM libraries -- it helps you run commands from NPM packages without installing it globally.
Here, I wanted to create an app called shelf
, but you can pick your own name. Once we run the command, Remake will make a new folder named shelf
(or a name you picked) with a starter Remake app inside. If you’ve used Create React App, this might feel familiar.
To test out the starter app, we can cd
into the new project and run the dev server:
cd shelf
npm run dev
Remake will take a second to build the app, and start running the app at localhost:3000
. Visit the URL on your computer to see the starter app, which is a Trello clone. You can also find this demo on the Remake website. It should look like this.
Try using the starter app to get a feel for how Remake apps work. Try adding, removing, and saving changes. Here are a few things I noticed in my brief tour.
- You can click on titles of stacks or task items to edit the text inside.
- When you edit some text, a modal opens with buttons to save, cancel, or (sometimes) delete the item.
- You can drag-and-drop to reorder items in a list.
This is no Netflix, but there’s enough here to build many kinds of apps. Adding, removing, and changing things are the building blocks for everything from blogs and todo lists to personal dashboards.
Let’s see what code makes this app possible by visiting app/pages/app-index.hbs
.
In the app-index.hbs
file, which is a Handlebars template, we’ll find an HTML template sprinkled with attributes like data-o-type
and data-i-editable
. You might have a guess as to what some of these attributes do, but for now, all we need to know is that these attributes are key to how Remake associates your app’s data with the template.
When you change the template and reload the page in your browser, you should see any changes in the template now reflected in the page. Try tweaking a few things before we explore the rest of Remake, like changing the “Add stack” button’s text.
Before diving into Remake’s nitty-gritty, we should understand what goes where in a Remake project.
Every web app needs to serve assets like images, JavaScript files, and CSS stylesheets. These are saved in app/assets
under their respective repositories. When Remake builds your app, these files are copied or compiled to /css
, /images
, /js
, and so on.
If you’ve been following along thus far, you might have a good guess about what these files do -- they’re templates for pages in your app.
For now, we only need to worry about app-index.hbs
, but here are what the other pages do.
index.hbs
: The “index page” of your app, when the user isn’t logged in (as opposed toapp-index
, for when the user is logged in).user/
templates: Pages related to account management, like login/sign up pages and password reset.
Most of the time, you’ll be editing app-index.hbs
and adding to that directory in Remake.
You might have noticed that pages in app/pages
don’t contain HTML boilerplate code like the page header. This is the responsibility of app/layouts/default.hbs
, which defines the app “shell” into which all your pages are rendered by Remake.
Remake stores user data in _remake-data
as JSON files. For example, for my user account with the username “thesephist”, Remake will create a thesephist.json
in which to store all my account data.
If you’re used to storing data in a relational database like Postgres, this tradeoff means a few things.
- Inspecting, editing, and debugging data is trivial, because you can just open the files to see how Remake sees your data.
- On the downside, storing data in files doesn't necessarily provide the same scalability and durability guarantees as a production-grade database.
For the kinds of apps I’m making for myself and a small number of users, most of the time, the advantages end up outweighing the costs.
Remake started “clicking” for me when I learned that most of building a Remake app is finding a way to map a user’s data to parts of the user interface. I call this thinking in Remake.
When a user requests a page from a running Remake app, what actually happens?
After authenticating the user, Remake…
- Fetches the data file (
user-name.json
) for this user - Finds the Handlebars template for the requested page
- Renders the page template with data from the user’s data file, and sends it to the browser, along with small CSS and JavaScript bundles for Remake’s UI.
- The browser then loads the page, and Remake’s scripts crawls the page for any elements annotated with Remake’s attributes to understand how the UI maps back to the user’s data. When the user makes any edits to the page, the annotations tell Remake how to save those changes back to the backend.
A key piece of the puzzle to understand is that the template tells Remake how to render the page from the data; Remake attributes tell Remake how to save data back from the page’s UI.
Because of this, a good first step to building a Remake app is to map out how your user’s data connects to your UI. For my Shelf.page app, I started with a schema like this:
User {
display_name: String
bio: String
topics: Array<{
name: String,
links: Array<{label, url}>
}>
}
A user has a display name and a short bio, and owns a list of topics; each topic has a name for the topic, and a list of links under it.
This shape of data maps really nicely to Shelf.page’s UI.
Once we’ve sketched this out, the work that remains is to express this in code! First, we write a Handlebars template for this page -- this lets Remake render our page.
Second, we add Remake attributes to our template, so Remake can save any edits. This is the topic of our last section.
Remake attributes have a lot of flexibility, but to get started, you’ll probably use them in a handful of useful combinations.
Most commonly, you’ll want to make a text field editable. In our example, we might want the user to be able to edit the display_name
property of our user data object. This takes three attributes.
<div data-o-type=”object”
data-l-key-display_name
data-i-editable-without-remove>
{{ display_name }}
</div>
Here, we tell Remake we’re talking about a property on an object (data-o-type=”object”
), specifically the property display_name
(data-l-key-display_name
). Then we make this field editable, but not removable, with data-i-editable-without-remove
.
The next common case is some way to add an item to a list of things. For example, you might want to add a task onto a todo list. If we have data that looks like below…
{
topics: [
{ name: ‘Computer science’ }
{ name: ‘Writing’ }
]
}
...and we want to show a list of editable topics, we’ll use these attributes.
<div data-o-type="list" data-o-key="topics">
{{#for topic in topics}}
<section
data-o-type=”object”
data-l-key-name>
<div data-l-target-name
data-i-editable>
{{ topic.name }}
</div>
</section>
{{/for}}
</div>
There’s a lot going on here, so let’s break it down. As you follow along, consider how the hierarchy of the template matches the hierarchy of our data.
- At the top, we tell Remake we have a list (
data-o-type=”list”
) of things at the property “topics” (data-o-key=”topics”
). Within thisdiv
, Remake will consider the list the “root” of our data. - We use Handlebars template notation to render a list of things
{{#for topic…}}
- Each
section
element corresponds to a single item in the list of topics, with is an object in our data. We tell Remake this withdata-o-type=”object”
.data-l-key-name
tells Remake to “link” this element to a particular editable field, which will come in handy next. - Finally, we have an editable field for our topic names. We make this field editable and deletable with
data-i-editable
. What happens when we delete this field? Because we linked this editable field to thesection
element withdata-l-target-name
matching thedata-l-key-name
, deleting this field will delete the whole topic (the wholesection
element).
The data-l-key/target
Remake attributes are the key to this pattern. It tells Remake to link one editable field to another part of our template.
The last pattern to know is a button to add more items to a list. Given our template for a list from before, adding a button is straightforward.
<div data-o-type="list" data-o-key="topics">
{{#for topic in topics}}
( ... )
{{/for}}
</div>
<button data-i-new=”topic”></button>
Here, we added an element, outside of our loop with the Remake attribute data-i-new=”topic”
, which tells Remake that clicking this button should add a topic
item to our templated list of topics.
These patterns were enough for me to build Shelf.page, and will help you get started making Remake apps. But if you’re interested in the full breadth of attributes offered by Remake, you can check out Remake’s data attributes documentation.
Once we have the basics of the app working with a template, there are a few finishing touches you might want to add. You might also run into some bugs you’re not sure how to start fixing. Here are a few tips I picked up shipping my first Remake project.
- JavaScript. Remake will compile your JavaScript with Babel before placing them in
/js
. If your JavaScript code isn’t doing what you expect, you might want to check that the compiler isn’t doing something you don’t expect. - Layouts. If you want to customize the “shell” of your app outside of the contents of page templates, you can update
app/layouts/default.hbs
to add any stylesheets, script tags, or metadata you need. - Debugging data. If you suspect Remake isn’t saving your data the way you expect, a good place to check is
_remake-data/user-app-data
, to see how Remake is saving your changes. If the data in this file isn’t shaped the way you expect, you could narrow down your search for the buggy parts of the template.
Once you have a working Remake app, deploying is fast and simple on Remake’s deployment platform. From the root of your project folder, simply run the command
npx remake deploy
and Remake will copy the right files up and spin up your service! If this is your first time deploying, Remake might ask you to create an account.
That’s it! You’ve deployed your Remake app, hassle-free. Try it out by going to your new domain and creating an account!
We’ve explored the basics of Remake in this post, but Remake is still growing and improving. For Remake the framework, a cleaner project layout is in the works, with nicer error messages and better documentation. For Remake the tool and deployment platform, you can expect new features like custom domain support in the future.
I’m really excited by how much faster Remake can make my prototyping work, and looking forward to how Remake grows in the future. If you, like me, have a few too many ideas for the hours in a day, consider building out your next hack on Remake.