🗓️ Simple Booking App Tutorial (No Double Bookings)

What’s going on Gliders!

A member of Loqode School recently had a question about booking/scheduling functionality in Glide, so I thought I’d share here too.

For this particular use-case the time slots are exactly 30 minutes each and span from 8am to 4pm… But you can very easily tweak this solution to suit your needs (e.g. variable length bookings).

Of course there are plenty of alternate ways to achieve the same functionality, but this appears to be a very simple option. It utilises what is known as a “Helper Table” (shoutout to @Darren_Murphy) to check for double-bookings on the fly and only allows users to select time slots that are free.

Enjoy! :clinking_glasses:


I did a similar App…

I see we have the same interest in making Apps :wink:

1 Like

Interesting! Did you use a similar method?

Kind of… same idea… but more advanced… I have to match my time zone and users… to rearrange inline lists for options and calendar view… and do a lot of HTML to make UI more friendly :wink:

1 Like

Awesome :+1:
Just shows that with a bit of creativity you can build just about anything in Glide :nerd_face:

1 Like

Very nice - love your tutorials, always clean and easy to follow.

A couple of comments/tips, because you know I can’t help myself :wink:

  • Those two template columns you have in the Helper table for the UserID and Name - it appears that you don’t actually use them anywhere? (or did I miss something?)
  • When you open the form, you use a Show New Screen action. Whilst that is fine, it does limit you to only being able to create bookings whilst in the context of that screen. What I like to do is create a Single Value → First → Helper Table → Whole Row in the User Profile table, and then use a Show Details (not Show New) screen via that column. Doing it that way essentially gives you a reusable Custom Form that you can call from anywhere in the App.
  • Clearing the User Specific columns in the Form. I like to do this as the form is opened, rather than when the form is submitted. Functionally it is the same, but it does eliminate the possibility of any race conditions where the columns might be cleared before the new row is created.
  • Date Formatting: this is one to watch out for, especially when you are using Dates in relations. The thing is, every users device could format the dates in a slightly different way depending on their device/browser regional settings, and this can result in unexpected (and broken) behaviour. The best way to avoid that is to first convert the dates to an integer: Year(Date) * 10000 + Month(Date) * 100 + Day(Date), which gives you something like 20230615. And then use that in your template. So that would become 20230615 8:30 am. Doing it that way will guarantee that your relations will always work for all users.

Thanks Darren, glad you enjoy them :popcorn:

And I’d expect nothing less! Always appreciate your input.

  • Your 100% correct in saying the UserID and Name columns aren’t used… Forgot to remove them when simplifying the app, and while recording I went on full auto-pilot mode explaining everything column by column without the penny dropping that they were now redundant :rofl: You have a keen eye!
  • Thats a great idea, thanks for explaining :+1:
  • Makes sense and it’s a very simple change to make.
  • I’d normally use Luxon (Other > Date & Time > Format Date) to standardise the dates but I kept it simple for the tutorial in order to get the point across, but you’re correct in pointing that out :pray:
1 Like

I’ve even had inconsistent results with that one in the past. When it was first added to Glide it seemed to be particularly buggy so I got turned right off it. It might be better now, I’m not sure. I just avoid it because I find that I can almost always get what I need with a bit of Date math :man_shrugging:


Interesting! I’ve used it sporadically for the past 5-6 months and haven’t come across any bugs yet :crossed_fingers: but I’ll definitely keep an eye out. I wonder if anyone else has dealt with issues/inconsistencies with it lately… :thinking:

It is always an issue… I would rely on Java and transform to Glide… for the general uniform results

I had a bit of a rant about it below:


In the real world :rofl:

Thank you for pointing it out though, good information to have on hand. I’ll watch for any oddities in the future… And if it’s relation-based and mission critical then something like your simple math solution may be the safest bet.


Great tutorial @Loqode! Thanks for sharing :pray:

How would you approach this issue…

If two or more people are using the app at the same time and select the same booking slot… at the end of the flow I assume only one will win.

How would you temporarily disable a time slot while someone is filling out details and release it if they back out?

Maybe set a timestamp when they enter the slot and give ‘em 5 min?


the flush app… solution… lol (plus clever script to deal with it)

I agree!

I have used both methods for a long time and they both work fine so far. In the beginning, when the Format Date plugin did not exist, the trick to convert the dates to an integer was something mandatory but I can bet that it works without problems currently.


Furthermore, I think that since the “Respect Time Zones” feature was released, any relation that uses 2 date columns will work just fine if both have the “Respect Time Zones” feature enabled so, there is no need to use the trick.

Technically, which method is faster to sort/lookup data if the key field is a number or a text? … a number!

When your dataset is small, you can’t see the difference in response time, but if you have more than 100k+ rows, the improvement is noticeable. In short and as advice: do not rule out the trick at all! :wink:

Saludos a todos!


Thanks Eric! Happy to share.

Here’s one way of doing it :thinking:

I wouldn’t use timers to temporarily disable it etc, what I would probably do is add a simple if/else check in the action that’s triggered on Submit.

Here’s how I’d set that up:

  • Add a Lookup column named Validation/All Bookings to the Booking Form table.
  • Select Bookings > Date & Time > Full as the value.
  • Add an Array Contains Element column named Validation/Date Taken next to it.
  • Select Validation/All Bookings as the values and Date & Time > Full Selection as the “To Find”.
  • Run an if/else scenario in the action that is submitting the booking.
    • If “Date Taken = Not Checked” then proceed as usual.
    • Else show notification to select another time (e.g. Someone has just taken this slot, please select another time).

Hopefully that makes sense! Not sure how quickly the Lookup column would be updated with the newly added booking, but off the top of my head this is how I’d do it.

Great to hear Gustavo! Thank you for sharing. And it definitely makes sense to consider any/all performance or reliability improvements when the datasets start getting substantial :ok_hand:

1 Like

In this case, no. You would actually wind up with duplicate bookings. Last man in only happens when you have two or more users trying to update existing rows. In this case we are adding new rows - and there is nothing going on server side to detect/prevent duplicates - so that’s what we’ll get.

I’m afraid that won’t work. The challenge is that all of those calculations happen independently on each users device, and potentially at the same time. So every user could perform that check, and because they all haven’t synced up yet, they all pass, and you still wind up with duplicate bookings.

The only way to reliably deal with this situation is to delegate the allocation of the bookings to a 3rd party, eg. Make. Then they can be queued and handled sequentially, with a Make scenario checking for duplicates before adding each new booking.


Thank you for the detailed follow up but let me clarify. I think no two people should be looking at the same time slot in the first place.

If there is 1 seat left on the plane and I click it on it…I should be the only person who can book that ticket for either some amount of time or until I back out.

In Glide determining when someone backs out can be tricky because you can close overlays, click tabs, etc… without the ability to trigger an action.

1 Like

What about making the slot unavailable because someone is simply looking at it…”reserved” if you will… same process? That could be a lot of rows?

Maybe use something like Make to set a helper table with timestamp + userID in booking slots and clear it after xx:xx time passes :man_shrugging:

1 Like