About the project

I was approached by a client who wanted to create a website offering various activities for tourists in Prague. After working together to concretize the idea and the clien­t's re­quire­ments, we went through a few iterations to find a suitable design.

I then built the website using React, Gatsby.js and SCSS on the frontend and Node.js with Express.js on the backend. After the website was built, I took care of its de­ploy­ment and occa­sion­al maintenance. Later, I also made a few smaller im­prove­ments, such as adding the ability to filter activities by category.

Some interesting parts of the project were making sure that the layout of the website was responsive and mobile-friendly, implementing support for multiple languages (although this func­tion­al­i­ty isn't currently in use because the content hasn't been translated yet), and building a booking form whose input fields can be individually configured for each activity.

The result

The result of this project is a live website that is being used by real customers to book ac­tiv­i­ties. You can take a look at the finished website for yourself. At the moment there is only English content on the site and translated content remains to be added, so un­for­tu­nate­ly it's not possible to try out the support for multiple languages.

The homepage of the website.
The homepage of the website

My approach

Analyzing requirements

After working with the client to clarify their ideas and needs for the website, we identified the following main requirements:

  • The site should support multiple languages to better accommodate tourists from different countries.
  • The design should be simple and focus on showcasing the activities.
  • The website should be fast to load and mobile friendly.
  • It should be easy to expand the func­tion­al­i­ty of the website in the future. For example, it should be possible to add a payment gateway in the future to allow customers to make payments directly on the website.
  • Updates to the website's content won't be very frequent.

With these requirements in mind, there were two possible approaches to building the site: either use an existing website builder or develop a custom website. Un­for­tu­nate­ly, the number of website builders that had built-in support for localization and the possibility of adding online payments in the future was very limited, and none of them fully met the client's expectations, so in the end we decided to go the route of a simple custom website. This solution also allows to customize the booking forms exactly to one's needs and generally allows for more control over the look and func­tion­al­i­ty of the site.

We also agreed that it wouldn't be worth it to make the website editable by the client for the time being, because content updates would be infrequent and it wouldn't be worth the cost and time to build a full admin interface or integrate an existing CMS.

The design process

After clarifying the overall organization of the website and the structure of its individual pages, I made a few mockups of possible design directions. The client wanted the website to have a simple and minimalist appearance (he cited the look of Pinterest as an example) and wanted the main focus to be on the activities themselves.

Exploring different design directions.
Exploring different design directions

Through several iterations, we arrived at a clean and minimalist design that prominently features images to help users visualize what it is like to participate in the offered activities. The activity details page also includes different types of media as part of the description, such as images, videos, maps, and more.

Example of Activity page.
The activity page features images and other kinds of media

Part of the design was also the creation of a simple logo that fits well with the website's design, with variations for different applications, such as the website's favicon.

Building the website

For the frontend of the website I decided to use React in combination with Gatsby.js because I was quite familiar with React and the site had no dynamic content, so it could be statically gen­er­at­ed. The backend runs on Node.js and uses Express.js to serve the frontend and pro­vide an API for submitting the contact and booking forms. Using Ja­va­Script on both the fron­tend and the backend made it possible to share parts of the code between them, for example by reusing the same logic for both client-side and server-side validation.

An interesting part of this project was building in support for multiple languages (although it isn't currently in use because the client hasn't provided translated content yet). Support for localization required hooking into Gatsby's content loading system to add metadata about the content language and generate localized page URLs, extending the built-in routing sys­tem to support localized URLs, implementing a system for translating text that is part of the website's layout, and more.

Since the booking and contact form is arguably the most important part of the site, I made sure to give it some extra care. This included user-friendly input val­i­da­tion, making the sub­mit button react to the current form submission state, and saving un­sub­mit­ted form data to localStorage (so the user doesn't lose their progress if the page is accidentally reloaded or closed).

Animation of contact form validation and submission.
Client-side input validation and form submission

The booking forms for each activity are configurable and allow adding different input fields for each activity. The forms also include basic spam reduction measures: a hon­ey­pot input and a control question, which are au­to­mat­i­cal­ly hidden if the user's web brows­er has Ja­va­Script enabled. So far, this strategy has worked surprisingly well to keep out spam bots while not bothering users with additional work.

Deployment and maintenance

Once the website was built, there were a few more things to do. To deploy the website, I had to set up the domain and web hosting (I went with Roští.cz, which provides SSH access to a container running NGINX and Node.js). A new task for me was to properly set up everything around sending emails, so that they are trust­wor­thy and don't get rejected as spam (for anyone who has done this before, terms like SPF, DKIM or DMARC are probably familiar).

Once the website was up and running, I took care of occasionally up­dat­ing the content, updating some dependencies, and making further smaller improvements, such as adding the ability to filter activities by category.

What I learned

Thanks to this project, I had the opportunity to go through all the phases of building a web­site, from the client's initial idea, through requirements analysis, design and development, up to deployment and maintenance.

In hindsight, I probably could have saved myself some work and brought even more value to the client by taking a more active role in the early stages of the project. Although I was hired primarily as a designer and developer, I should have started by helping to define a clear busi­ness plan and high-level goals for the project before gathering specific re­quire­ments for the website's design and func­tion­al­i­ty.

While building the website, I learned a lot about in­ter­na­tion­al­iza­tion and localization, in­clud­ing how to properly spell "in­ter­na­tion­al­iza­tion" and how to implement localized routing and linking. Even though Gatsby.js didn't support localization out of the box, I came to appreciate its very customizable and flexible API, which allowed me to hook into almost any part of the framework and adapt it to the needs of the project.

This project also helped me realize how quickly some JavaScript frameworks and libraries change. Among other things, I had to go through a major version upgrade of Gatsby.js in the middle of de­vel­op­ing the website. My takeaway is to always consider whether a dependency is really necessary and adds enough value to the project to be worth it.

Last but not least, I also learned a thing or two about building responsive websites, as evidenced by the fact that I can already see some things I would do differently next time.