File structure

| 1 min read | JavaScript Framework Diet 5/10

In the previous posts, we’ve gone through our first few utility functions. We now have enough in our toolbox to move on to our first component. However, where do all these functions belong?

Let’s get straight to the point. Here’s what the structure of my vanilla JS projects looks like:


On the top level, we have one file and two folders. app.js is the script that will eventually end up in our users’ browsers.

<script src="/js/app.js" defer></script>

99% of the time, I include scripts in the head of the document with a defer tag. This means the script is loads and parses while the rest of the document is still loading. The script only gets executed after the document is ready.

The util folder contains utility functions; these aren’t tied to DOM elements but are everyday helper functions that we’ll use to build components. We’ve already reviewed a few utilities in the previous posts: $, $$, and listen.

The utility functions are split across multiple files to keep them manageable, although in smaller projects, you can start with a single util.js file and extract where necessary.

// util/query.js

export function $(selector, scope = document) {
    return scope.querySelector(selector);

export function $$(selector, scope = document) {
    return Array.from(scope.querySelectorAll(selector));

util/index.js acts as the entry point for all utility functions.

// util/index.js

export * from './listen.js';
export * from './query.js';

In component scripts, we can then easily access all utility functions with a single import, without worrying where they belong on the file system.

// components/dropdown.js

import { $, listen } from '../util';

Don’t worry about the rest of the contents of a component script yet, that’s for the next post in this series.

Components are then imported in app.js.

// app.js

import './components/datePicker';
import './components/dropdown';

Components with large dependencies can be code split with Webpack to keep the initial bundle size smaller.

// app.js

import { $ } from './util';
import './components/dropdown';

if (document.querySelector('[data-date-picker]')) {

As a project grows, this structure might get unwieldy over time. In that case, you could split the components folder into subfolders.

We now have enough knowledge to build our first component: a dropdown list!

JavaScript Framework Diet

JavaScript frameworks are great, but overused. Adding small bits of interactivity to an interface shouldn’t mean installing kilobytes of dependencies or introducing complex build tools.

It’s time for a diet. I’m challenging you to build something without a framework, or follow along and learn something along the way.

Series under development, posts are added on a weekly basis.

  1. Introduction
  2. Selecting elements (part 1)
  3. Selecting elements (part 2)
  4. Event delegation
  5. File structure
  6. Dropdowns
  7. Enter and leave transitions
  8. Modals (part 1)
  9. Modals (part 2)
  10. Live updating data


I occasionally send out a newsletter with personal stories, things I’ve been working on in the past month, and interesting things I come across.

Pinky swear that I won't use your data for any other purposes. Your email address will be stored on Mailchimp and nowhere else.

More about JavaScript & Vanilla JS

Webmentions ?