Want to take your software engineering career to the next level? Join the mailing list for career tips & advice Click here


:date: Make a beautiful embeddable booking widget in minutes

Subscribe to updates I use booking-js

Statistics on booking-js

Number of watchers on Github 2191
Number of open issues 40
Average time to close an issue 18 days
Main language JavaScript
Average time to merge a PR 3 days
Open pull requests 11+
Closed pull requests 2+
Last commit over 2 years ago
Repo Created over 4 years ago
Repo Last Updated over 2 years ago
Size 6.75 MB
Homepage http://booking.ti...
Organization / Authortimekit-io
Latest Releasev1.24.2
Page Updated
Do you use booking-js? Leave a review!
View open issues (40)
View booking-js activity
View TODOs for booking-js (2)
View on github
Fresh, new opensource launches 🚀🚀🚀
Software engineers: It's time to get promoted. Starting NOW! Subscribe to my mailing list and I will equip you with tools, tips and actionable advice to grow in your career.
Evaluating booking-js for your project? Score Explanation
Commits Score (?)
Issues & PR Score (?)

Booking.js by Timekit

Circle CI

Releases & changelog

Make a beautiful embeddable booking widget in minutes.

Booking.js Screenshot

Uses FullCalendar with a custom theme for dynamic calendar rendering with available timeslots fetched from Timekit (through the Javascript SDK). The shown appointment slots can be booked with automatic calendar invites sent to both host and visitor. Integrates with Google Calendar for automatic availability.

Maintainer: Lasse Boisen Andersen (la@timekit.io). PR's are welcome!

Get started

Visit Timekit.io to get started!

You can use the widget in two different ways:

  1. For non-developers where the widget is configured in our UI admin panel. For this, you'll never need to touch any code - just follow the admin panel to get your embed code or hosted url.
  2. For developers where you can configure the widget on-the-fly for multi-user usage and more control. In this way, you don't need to generate a widget for each user through the Timekit admin. Timekit provides you with a modular and flexible API platform that allows you to integrate availability and bookings deep into your own product.

This repo is mainly for community contributions, docs and the curious soul that would like to customize the widget.


Stuff you need to load:

  • jQuery (v2 - v3) - primarily because it's a requisite for FullCalendar

Bundled together with the library:

  • fullCalendar - a customizable and flexible event calendar built for the browser
  • moment - parse, validate, manipulate, and display dates in JavaScript
  • timekit-js-sdk - JavaScript SDK for the Timekit.io API

Module loading


To ensure that we can push out updates, improvements and bugfixes to the library, we recommend that you load the library through our CDN. It's hosted on Amazon Cloudfront so it's snappy enough for production.

<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script type="text/javascript" src="//cdn.timekit.io/booking-js/v1/booking.min.js" defer></script>

UMD through NPM

The module is published on NPM and can be require'd as a CommonJS, AMD or in a script tag.
npm install timekit-booking

// Booking.js still needs access to jquery, so make sure its available
// Pull in the module, ES2015 imports also works:
// import TimekitBooking from 'timekit-booking'
var TimekitBooking = require('timekit-booking')
// Booking.js is now available as local var TimekitBooking instead of global window.timekitBooking
var widget = new TimekitBooking();



The simplest and most universally compatible usage is with autoload. This will defer the loading of the library until the whole document has loaded and then look for window.timekitBookingConfig - if found, the library is loaded automatically. Note that in single page applications, you should not use this approach (see next section).

<div id="bookingjs">
  <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
  <script type="text/javascript" src="//cdn.timekit.io/booking-js/v1/booking.min.js" defer></script>
  <script type="text/javascript">
    window.timekitBookingConfig = {
      app:      'back-to-the-future',
      email:    'marty.mcfly@timekit.io',
      apiToken: 'bNpbFHRmrfZbtS5nEtCVl8sY5vUkOFCL'


If you intent to run multiple instances or want more control over initialization order, create a new instance. This is ideal for usage in single page applications like Angular.js, where you'd injecting <div id="bookingjs"> async in your template and control it through JS in your controller/directive/component code.

<div id="bookingjs">
  <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
  <script type="text/javascript" src="//cdn.timekit.io/booking-js/v1/booking.min.js"></script>
  <script type="text/javascript">
    var widget = new TimekitBooking();
      app:      'back-to-the-future',
      email:    'marty.mcfly@timekit.io',
      apiToken: 'bNpbFHRmrfZbtS5nEtCVl8sY5vUkOFCL'


The widget connects to the Timekit API behind the scenes and requires a Timekit account.

You can either connect with a Google account (recommended) or create a plain account (you'd have to enter availability and pull out events through the API).
Visit the setup wizard on Timekit.

The app, email and apiToken settings are the key part here. When you've registered an app, created a user and generated a widget credentials, you get a special client-token with limited access. It's only capable of hitting certain endpoints so your account stays secure when using the widget in a public browser environment.


Booking.js is made for various use-cases, so it's really extensible and customizable. We augment all the intrinsic options so you can overwrite them as needed, e.g. Timekit FindTime options or FullCalendar settings.



  // Required
  app:                      '',   // Your Timekit registered app slug
  email:                    '',   // Your Timekit user's email (used for auth)
  apiToken:                 '',   // Your Timekit user's apiToken (as generated through the wizard)

  // Optional
  name:                     '',   // Display name to show in the header and timezone helper
  avatar:                   '',   // Provide an image URL for a circular image avatar
  calendar:                 '',   // Your Timekit calendar ID that bookings should end up in (defaults to primary calendar)
  targetEl:                 '#bookingjs', // Which element should we the library load into
  autoload:                 true, // Auto initialization if config object is found on window var
  goToFirstEvent:           true, // Display and scroll to the first upcoming event in the calendar (to avoid showing a blank calendar)
  bookingGraph:             'instant', // Set which booking flow graph that should be used (also supports "confirm_decline", see below)
  availabilityView:         'agendaWeek', // Defines the timeslot view (also supports "listing" for a list view)
  debug:                    false, // Enable debugging mode to output useful state/step data in the console

  // Timekit JS SDK (see below)
  timekitConfig:            { ... },

  // Timekit FindTime endpoint (see below)
  timekitFindTime:          { ... },

  // Timekit CreateBooking endpoint (see below)
  timekitCreateBooking:     { ... },

  // Locale support presets (see below)
  localization:             { ... },

  // FullCalendar options (see below)
  fullCalendar:             { ... },

  // Customize form fields (see below)
  bookingFields:            { ... },

  // Register callbacks on events (see below)
  callbacks:                { ... }


- timekitConfig

You can pass any of the Timekit JS SDK settings directly to the widget. This is mostly relevant if you're building a tighter integration with Timekit and have your own app registered on the platform.

As a shorthand, the app slug can be set using a root-level app config key too.

timekitConfig: {
  app:          'your-app-slug-here' // Specify your app slug

- timekitFindTime

The Find Time algorithm is a powerful query tool for availability. Booking.js is calling the endpoint [POST] /findtime through the JS SDK and takes all the arguments as mentioned on the official docs. The most powerful aspect are the filters. By default, there's no filters applied.

There's only three default arguments out of the box:

timekitFindTime: {
  future:       '4 weeks',      // Default, how long time into the future that timeslots should be returned
  length:       '1 hour',       // Default, the duration of the bookable timeslots
  emails:       [config.email], // Inserted dynamically from the "email" setting in the general config

- timekitCreateBooking

When booking an event, the widget will call the [POST] /bookings endpoint through the JS SDK.

The booking engine in Timekit is a powerful state machine that can take input data (event details and customer info) and perform actions on that based on a chosen flow graph.

At the time of writing, Timekit supports two graphs:
1) instant - automatically confirm any incoming bookings, save it to calendar and send out notifications
2) confirm_decline - creates the booking as tentative and send notification to owner with confirm/decline actions

These can be set using the bookingGraph config key on the root config level.

You can override specific endpoint settings like so:

timekitCreateBooking: {
  graph:          'instant',       // Inserted based on "bookingGraph" specified. See description above on flow graph
  action:         'confirm',       // If "instant" graph is chosen, it will instantly perform the "confirm" action. See description above on flow graph
  event: {    
    where:        'Online',        // Default, you may want to customize this to a specific location, TBD or whatever fits
    invite:       true,            // Default, makes sure that participants (the visitor) is sent a Google invite
    my_rsvp:      'accepted',      // Default, makes sure that the host has accepted the created event in Google
    start:        data.start,      // Inserted dynamically from the chosen timeslot
    end:          data.end,        // Inserted dynamically from the chosen timeslot
    what:         config.name + ' x '+ data.name, // Inserted dynamically based on the host and visitors names (you can replace it with a static string)
    calendar_id:  config.calendar, // Inserted dynamically from the "calendar" setting in the general config if present
    participants: [config.email, data.email], // Inserted dynamically based on host and visitors ()
    description:  data.comment || '' // Inserted dynamically based on the visitor's supplied comment (if field is enabled)
  customer: {
    name:         data.name,       // Inserted dynamically based on visitors name
    email:        data.email,      // Inserted dynamically based on visitors email
    timezone:     moment.tz.guess() // Inserted dynamically based on visitors timezone (sniffed from browser/device settings)

- fullCalendar

You can supply and override all the FullCalendar settings:

fullCalendar: {
  header: {
    left:       '',
    center:     '',
    right:      'today, prev, next'
  views: {
    agenda: {
      displayEventEnd: false
  allDaySlot:   false,
  scrollTime:   '08:00:00',
  timezone:     'local',
  defaultView:  sizing.view,     // Inserted dynamically based on the current width of the widget
  height:       sizing.height,   // Inserted dynamically based on the current width of the widget
  eventClick:   function(event), // Handled internally in Booking.js (overwrite if you want to replace the booking page)
  windowResize: function(view)   // Handled internally in Booking.js (overwrite if you want to disable or change automatic resizing)

See below for FullCalendar language support.

- localization

For quick localization of time/date formats, we provide a simple preset setting, timeDateFormat, that sets a range of different FullCalendar and localization settings.

By default, it's set to 12-hour clock, M/D/Y date format, Sunday first day of week (12h-mdy-sun). It can be changed to 24-hour clock, D/M/Y date format, Monday first day of week (24h-dmy-mon).

See /examples/local-preset.htm

localization: {
  showTimezoneHelper: true, // Should the timezone difference helper (bottom) be shown?
  timeDateFormat: '12h-mdy-sun', // US-style per default. For EU-style formatting, use '24h-dmy-mon'
  bookingDateFormat: 'MMMM D, YYYY', // Override the default date format on the booking page
  bookingTimeFormat: 'h:mma' // Override the default time format on the booking page
  strings: { ... } // See below

For full language support, FullCalendar also takes a locale option, accompanied by a language file. Make sure to use defer attribute on a script tag loading the language file if you are deferring booking.js, language file should be loaded after booking.js, but before initialization.

Remember to set localization.timeDateFormat to false so it doesn't override the language file's settings.

See /examples/local-language.htm

fullCalendar: {
  locale: 'de'
localization: {
  timeDateFormat: false

If you're using the widget in another language, you might want to customize the text strings used in e.g. submit button and success message. This can be done in the localization.strings key.

See /examples/local-strings.htm

localization: {
  strings: { // Customize string used in the widget, defaults are shown below
    allocatedResourcePrefix: 'with',
    submitText: 'Book it',
    successMessageTitle: 'Thanks!',
    successMessageBody: 'An invitation has been sent to: <br /> %s <br /><br /> Please accept the invitation to confirm the booking.',
    timezoneHelperLoading: 'Loading..',
    timezoneHelperDifferent: 'Your timezone is %s hours %s of %s (calendar shown in your local time)',
    timezoneHelperSame: 'You are in the same timezone as %s'

- bookingFields

You can customize the booking form fields and their settings in this section. Only the name, email and comment fields are enabled by default. The name and email fields have to be enabled and is always required (for the event creation to work properly). All other fields can be enabled/disabled.

If you're collecting user information before loading the widget, it can be useful to inject it into the form by setting the prefilled keys - just pass in the values and they will be set upon load. Combine it with locked to lock the fields for user input.

See /examples/fields.htm

bookingFields: {
  name: {
    placeholder: 'Full name',
    prefilled: false,
    locked: false
  email: {
    placeholder: 'E-mail',
    prefilled: false,
    locked: false
  comment: {
    enabled: true,
    placeholder: 'Comment',
    prefilled: false,
    required: false,
    locked: false
  phone: {
    enabled: false,
    placeholder: 'Phone number',
    prefilled: false,
    required: false,
    locked: false
  voip: {
    enabled: false,
    placeholder: 'Skype username',
    prefilled: false,
    required: false,
    locked: false
  location: {
    enabled: false,
    placeholder: 'Location',
    prefilled: false,
    required: false,
    locked: false

- callbacks

You can hook into events happening throughout the user flow and perform asynchronous events. This is especially powerful for saving user data to your CRM system or redirect users to a payment gateway after booking is finished.

Inspect to source code to learn more about in which order callbacks are fired. Complete list:

callbacks: {
  findTimeStarted:          function(args) {},
  findTimeSuccessful:       function(response) {},
  findTimeFailed:           function(response) {},
  createBookingStarted:     function(args) {},
  createBookingSuccessful:  function(response) {},
  createBookingFailed:      function(response) {},
  getUserTimezoneStarted:   function(args) {},
  getUserTimezoneSuccessful:function(response) {},
  getUserTimezoneFailed:    function(response) {},
  fullCalendarInitialized:  function() {},
  renderCompleted:          function() {},
  showBookingPage:          function(event){},
  closeBookingPage:         function() {},
  submitBookingForm:        function(values) {},
  errorTriggered:           function(message) {}


After you instantiated the widget, you can control it with the following methods:

var widget = new TimekitBooking();
widget.init(config);          // Initializes the widget with the given config
widget.render();              // Re-renders the widget with it's instance config
widget.setConfig(config);     // Push a new config into it (call render() afterwards)
widget.getConfig();           // Returns the current config
widget.destroy();             // Cleans the DOM element and empty config
widget.fullCalendar(action);  // Direct access to FullCalendar's own method (for advanced use)

Only available when your using the instantiation approach and not autoload


See Issues for feature requests, bugs etc.

booking-js open issues Ask a question     (View All Issues)
  • over 3 years "Calendar / UI widget not working in Mobile"
  • over 3 years Removing Video Call Link and Invitee Phone Number from Google Calendar Invitation
  • almost 4 years Add the Ability to Style and Add Text to Booked Timeslots
  • almost 4 years Breaks on CommonJS/Browserified builds
  • about 4 years Multiple event slots - expose method showBookingPage() ?
  • about 4 years New duration values + Monthly calendar view
  • about 4 years Enable click on submit button once we display a "success"
  • about 4 years Support for confirmation emails in different languages
  • about 4 years antispam check
  • over 4 years Multiple selection
  • over 4 years Invite confirmation
  • over 4 years Select availabilities with Google Calendar
  • over 4 years Time slot booked multiple times bug?
  • over 4 years Support recurring bookings/appointments for e.g. subscriptions
  • over 4 years Support for "composite" (team) availability where anyone of x people are available
  • over 4 years Timeslot length should be selectable by the user
  • over 4 years Can we add reminder support?
  • over 4 years Create custom booking fields on the fly
  • over 4 years Security issue: Domain name restriction does not seem to be working
  • over 4 years Booking confirmation email should be sent to calendar owner too
  • over 4 years The calendar is displayed as day view although container is wide
  • over 4 years Facebook integration
  • over 4 years The location booking field should support Google places autocomplete
  • over 4 years Floating labels for inputs to enhance UX (especially with prefill)
  • over 4 years Ability to specify order of the booking form fields
  • over 4 years Multiple people can book one timeslots (tours, group events etc)
  • over 4 years Create video call event (Hangouts, Skype) inside Google Calendar
  • over 4 years In mobile view, empty days should show an explaining text
  • over 4 years Booking confirmation flow
  • over 4 years iCal file compatibility (in addition to Google)
booking-js open pull requests (View All Pulls)
  • New booking engine
  • Better error reporting/output
  • Fixes modal close icon in IE 11
  • Hour v2
  • Adds support for remote loading widget configs + hour re-renaming
  • Group bookings WIP
  • Added click to close on submit success as default behaviour
  • Added month view example
  • add es6-promise as dependency to support ie11 and lower
  • Project stop-the-bleeding
  • Minor cleanup
booking-js list of languages used
booking-js latest release notes
v1.24.2 v1.24.2

[2018-02-20 14:26 +00:00] Link to new API reference in readme by @petersuhm - https://github.com/timekit-io/booking-js/pull/173

v1.24.1 v1.24.1

[2018-02-14 09:39 +00:00] Fix async tests & misc by @laander - https://github.com/timekit-io/booking-js/pull/160

v1.24.0 v1.24.0

[2018-02-14 08:59 +00:00] Target calendar is optional by @laander - https://github.com/timekit-io/booking-js/pull/172

More projects by timekit-io View all
Other projects in JavaScript