Skip to main content
Local Favorites app screenshot - Header with logo and slogan, Body with Google Map showing selected result pin (United Nations Headquarters) and Sidebar with list of map results

Local Favorites

Local Favorites is a Vue.js web app which lets you save and review your favorite local places using the Google Maps JavaScript API.

The app features an interactive map of your local area and a list of nearby points of interest (stores, restaurants, parks, etc).

Technologies: Vue.js, Google Maps API, HTML5, CSS3

Cropped screenshot showing the results sidebar with the My Favorites tab selected, which shows a list of locations with star ratings and user notes

My Favorites

When you select a place from the list or click its icon on the map, you can save it to your favorites and add a review with a star rating. The review will be displayed on the map and on the list. If you saved the location as a favorite it will be displayed in the My Favorites tab.

When a user saves a favorite place or adds a review, their favorites are saved locally using the browser's LocalStorage so that the data can persist when the user returns to the website.

Google Maps JavaScript API

Local Favorites integrates many different services from the Google Maps JavaScript API to provide a fuller experience. The vue-google-maps component provides default implementation of some of the API services and the app builds upon that implementation for the functionality of the website.

When the app initializes it prompts the user's browser for their location using the Geolocation API via navigator.geolocation. This location is provided to the Maps Service to update the bounds of the visible map. The user can also use the autocomplete search input, which is added as a map control, to search for a specific place or general area using the Places Autocomplete Service.

When the bounds of the map have changed, the system will request a new list of nearby places using the Places Service. These places are displayed in the results list on the Nearby tab. Additionally, the user can click a place icon on the map to select a location.

Project Components

The project is separated into multiple single-file Vue.js components which provide their own internal templates, logic, and styling. The component .vue files are compiled using Webpack.

The project also utilizes a centralized state management pattern which places app state data management into the AppState.js file, so that the common state data can be accessed by separate components. This pattern made the component interaction easier to maintain overall, but could be replaced with a more standardized implementation like Vuex (see Future Updates section).


Local Favorites design screenshot showing the layout of the app including header logo and slogan, Google Map with selected result pin (Holmdel Park) and Sidebar results list

Local Favorites uses a Flexbox based design that allows the main section (the map and the results list sidebar) to fill the page vertically and horizontally on larger screens. The sidebar uses a maximum width of 400px and the map can fill the rest of the available screen width. The contents of the sidebar scroll instead of flowing beyond the screen height.

Click the video to pause or resume.

On smaller screens, the app uses a vertical design. The map has its maximum height to set to 250px and it fills the screen horizontally. The results list sidebar is then moved to below the map.

Some components make use of the Vue.js animation templates to provide motion to certain controls, as seen in the example screen capture. For example, when a place is selected the button controls slide into the screen. Also, when the active tab is changed the tab's content slides in and the other tab slides out.

The app uses some base styles from the Bulma CSS framework, but only imports the style modules required. This reduces the overall project CSS file size. Additionally SVG icons are used to add extra flair to buttons, links, tabs, etc.

Accessibility Considerations

The website aims to be accessible by keyboard, as well as mouse. The results in the sidebar list have been added to the tabindex so that they can be focused when hitting the Tab key. The results can also be navigated using the Up and Down keys. Once focused, a result can be selected by pressing the Enter or Space keys. The user can then use the Tab key to reach the Save and Add Note control buttons.

The app also uses the outline CSS property to add a highlighted look to controls when they are focused. The highlighted focus look improves visibility when using the keyboard to navigate, but doesn't look out of place when using the app with a mouse.

One issue that arose while reviewing accessibility is that the map doesn't seem to be able to release keyboard focus in Firefox, even if you keep pressing the Tab key. To avoid this issue, the sidebar was placed ahead of the map in the HTML code. The Flexbox CSS was updated to reverse the flow of the design so that the sidebar will still appear after the map. Now in Firefox, the user can tab through the results list and its controls before hitting the map element and losing focus.

Progressive Web App

The app was bootstrapped using the Vue CLI tool which provides the configuration necessary for the Progressive Web App standard. By following the PWA standard, the website can be saved to a user's device home screen and act as a native app.

The app uses a JavaScript Service Worker to facilitate caching the app resources, so the app will load quickly and can be used offline. The app would still need to be online to connect to the Google Maps API, but the saved My Favorites list and reviews could be viewed and updated.

Browser Support

Due to the use of Flexbox and the Vue.js framework, browser support is limited to modern browsers, as well as Internet Explorer 10 and 11.

Support for Internet Explorer 10 and 11 required the use additional polyfills included in the Webpack configuration. Polyfills are scripts that provide support for newer ECMAScript features in browsers that aren't compatible.

Future Updates

  • Smoother Results List Updates

    When the map bounds are changed, the app requests a new list of results from the Google Maps API. When a result is selected, that result is centered on the map and an InfoWindow is displayed on the map. This may cause a positioning issue where the map automatically readjusts the map bounds to fit the InfoWindow, which could result in a new list being requested.

    This can result in a jumpy experience where the list is refreshed multiple times in a short amount of time. One possible fix would be to check that the bounds haven't changed past a certain distance before requesting a new list.

  • Avoid Geolocation Request at Startup

    When the app is initialized, it requests the browser's geolocation, which requires a prompt asking for the user's permission. This experience could be improved by providing the map with a default location and adding a control to the map that requests the browser's geolocation on demand.

  • Better Offline Support

    If the system detects the user is offline or otherwise unable to connect to Google Maps, it should hide or minimize the features that require API connection and instead show the My Favorites tab, which can be used offline.

  • Replace App State Management Pattern with Vuex

    The centralized app state management pattern provided by the AppState.js file could be replaced with a more robust implementation like Vuex.

    A library like Vuex could improve the way the central app state is managed in terms of efficiency and structure, but would require the introduction of more boilerplate code and concepts specific to the library.