Quince
What is Quince?
Quince is an opinionated framework for building dynamic yet fully server-rendered web apps, with little to no JavaScript.
Inspired by
React, Turbo, Hotwire amongst others
Current status
Early, but working in production. Expect more features and optimisations to come, but also potential for big changes between versions in the early stages.
How it works
- Define some components and
expose
them at certain routes - Define some interactions that can take place, which can change the state of the components, and are handled with ruby methods
- The front end will swap out the updated components with new HTML re-rendered by the back end
Minimal 'hello world' example
# app.rb
require "quince"
class App < Quince::Component
def render
html(
head,
body("hello world")
)
end
end
expose App, at: "/"
- Run it via
ruby app.rb
- Visit
localhost:4567/
!
More complex example
require 'quince'
class App < Quince::Component
def render
Layout(title: "First app") {[
Counter()
]}
end
end
class Layout < Quince::Component
Props(title: String)
def render
html(
head(
internal_scripts
),
body(
h1(props.title),
children
)
)
end
end
class Counter < Quince::Component
State(val: Integer)
def initialize
@state = State.new(
val: params.fetch(:val, 0),
)
end
exposed def increment
state.val += 1
end
exposed def decrement
state.val -= 1
end
def render
div(
h2("count is #{state.val}"),
button(onclick: callback(:increment)) { "++" },
button(onclick: callback(:decrement)) { "--" }
)
end
end
expose App, at: "/"
Why Quince?
Why not?
- You have pre-existing APIs which you want to integrate a front end with
- You want to share the back end API with a different service
- You want more offline functionality
- You need a super complex/custom front end
Why?
- Components > templates ๐งฉ
- Lightweight ๐ชถ
- Shallow learning curve if you are already familiar with React ๐
- Focus on your core business logic, not routes/APIs/data transfer/code sharing ๐งช
- No compilation/node_modules/yarn/js bundle size concerns - just bundler ๐ฆ
- Get full use of Ruby's rich and comprehensive standard library ๐
- Take advantage of Ruby's ability to wrap native libraries (eg. gems using C) โก๏ธ
- Fully server-rendered responses - single source of truth ๐ก
- Easy to recreate/rehydrate a pages state (almost nothing is stored in memory from JavaScript - all the state is stored with the HTML document's markup)
Installation
Add this to your application's Gemfile:
gem 'quince'
And then execute:
$ bundle install
Or install it yourself as:
$ gem install quince
Usage notes
- All HTML tags are available via a method of the same name, eg.
div()
,section()
,span()
- with the exception ofpara
standing in forp
to avoid clashes with Ruby's commonKernel#p
method - All HTML attributes are available, and are the same as they would be in a regular html document, eg.
onclick
rather thanonClick
- with the exception of aClass
,Max
,Min
,Method
- which start with capital letters to avoid clashes with some internal methods. - Type checking is available at runtime for a component's
State
andProps
, and is done in accordance with Typed Struct - Children can be specified in one of two places, depending on what you would prefer:
- as a block argument, to maintain similar readability with real html elements, where attributes come first
div(id: :my_div, style: "color: red") { h1("Single child") } div(id: "div2", style: "color: green") {[ h2("multiple"), h3("children") ]}
- as positional arguments (for convenience and cleanliness when no props are passed)
div( h1("hello world") )
- as a block argument, to maintain similar readability with real html elements, where attributes come first
- A component's
render
method should always return a single top level element, ie. if you wanted to return 2 elements you should wrap them in adiv
Development
After checking out the repo, run bin/setup
to install dependencies. Then, run rake spec
to run the tests. You can also run bin/console
for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install
. To release a new version, update the version number in version.rb
, and then run bundle exec rake release
, which will create a git tag for the version, push git commits and the created tag, and push the .gem
file to rubygems.org.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/johansenja/quince.
License
The gem is available as open source under the terms of the MIT License.