# How to Import Libraries in Retool's JavaScript Editor

- Tool: Retool
- Difficulty: Beginner
- Time required: 10-15 min
- Compatibility: Retool Cloud and Self-hosted
- Last updated: March 2026

## TL;DR

To import JavaScript libraries in Retool, open App Settings (gear icon in the top toolbar) and add CDN URLs to the Preloaded JavaScript section. Libraries loaded this way are available as global variables (e.g., _ for lodash, moment for moment.js) in all JS Queries in the app. Retool also pre-bundles lodash and moment.js — they are available without any import configuration.

## Loading External JavaScript Libraries in Retool

Retool's JavaScript editor lets you write custom logic using any globally available JavaScript library. Retool pre-bundles several popular libraries — lodash (available as _), moment.js (available as moment), and others — so you can use them without any configuration.

For libraries not included by default, you can load them from a CDN (like jsDelivr or unpkg) by adding the URL to the Preloaded JavaScript section in App Settings. The library's script tag loads before your queries run, making any global it exports (like Chart, Papa, or dayjs) available in your JS Queries.

This tutorial covers loading libraries from CDN, using the pre-bundled lodash and moment.js, and practical examples of each in Retool workflows.

## Before you start

- A Retool account (Cloud or Self-hosted)
- Editor access to a Retool app (Edit permissions or above)
- Basic familiarity with writing JS Queries in Retool
- The CDN URL of the library you want to import (from jsDelivr, unpkg, or your own host)

## Step-by-step guide

### 1. Check if your library is already pre-bundled in Retool

Before adding a CDN URL, check whether Retool already bundles the library you need. Retool includes lodash (accessed as _ or lodash), moment.js (accessed as moment), numbro (number formatting), Papa Parse (CSV parsing, accessed as Papa), and several others. Create a quick JS Query and log _ to the console — if it prints the lodash object, it is already available. You do not need Preloaded JavaScript for these built-ins.

```
// Test in a JS Query — check which libraries are pre-bundled
console.log('lodash:', typeof _);        // 'object' if available
console.log('moment:', typeof moment);  // 'function' if available
console.log('Papa:', typeof Papa);      // 'object' if available

// Use lodash immediately if available
const grouped = _.groupBy(query1.data, 'category');
console.log(grouped);
```

**Expected result:** The console shows the type of each library. If 'object' or 'function', the library is pre-bundled and ready to use.

### 2. Open App Settings to access Preloaded JavaScript

In the Retool editor, click the gear icon in the top toolbar to open App Settings. In the left sidebar of the settings panel, look for the Scripts & Styles section (sometimes labeled Advanced). You will find two text areas: Preloaded JavaScript and Preloaded CSS. The Preloaded JavaScript section is where you add script tags for external libraries. These scripts load before any queries run when a user opens the app.

**Expected result:** App Settings panel is open and you can see the Preloaded JavaScript text area.

### 3. Add a CDN script tag for your library

In the Preloaded JavaScript text area, add a standard HTML script tag pointing to the library's CDN URL. Get the CDN URL from jsDelivr (https://www.jsdelivr.com) or unpkg (https://unpkg.com) by searching for the package name. Always pin to a specific version to prevent unexpected breaking changes when the library updates.

```
<!-- In App Settings → Scripts & Styles → Preloaded JavaScript -->

<!-- Load date-fns for modern date manipulation -->
<script src="https://cdn.jsdelivr.net/npm/date-fns@3.6.0/cdn.min.js"></script>

<!-- Load Papa Parse for CSV processing -->
<script src="https://cdn.jsdelivr.net/npm/papaparse@5.4.1/papaparse.min.js"></script>

<!-- Load numeral.js for number formatting -->
<script src="https://cdn.jsdelivr.net/npm/numeral@2.0.6/numeral.min.js"></script>

<!-- Load a custom analytics helper from your own CDN -->
<script src="https://cdn.yourcompany.com/retool-helpers/v1.2.0/helpers.min.js"></script>
```

**Expected result:** The script tag is saved in Preloaded JavaScript. The library loads when the app is next opened.

### 4. Use the library in a JS Query

After saving the Preloaded JavaScript setting, create or open a JS Query. The library is now available as a global variable — for example, dateFns, numeral, or whatever global the library exports. Reference it directly without any import statement. If you are unsure of the global name, check the library's documentation for 'UMD global name' or 'browser global'.

```
// Using date-fns (loaded from CDN as dateFns)
const rawDates = query1.data.map(row => row.created_at);
const formattedDates = rawDates.map(date =>
  dateFns.format(new Date(date), 'MMM d, yyyy')
);
return query1.data.map((row, i) => ({
  ...row,
  created_at_formatted: formattedDates[i]
}));

// Using numeral for currency formatting
const totalRevenue = query1.data.reduce((sum, row) => sum + row.amount, 0);
const formatted = numeral(totalRevenue).format('$0,0.00');
return { total: formatted };
```

**Expected result:** The JS Query executes without 'is not defined' errors and returns data processed by the library.

### 5. Use the pre-bundled lodash library for data manipulation

Lodash is available in all Retool JS Queries as _ (underscore) without any configuration. It is one of the most useful tools for transforming query data — grouping rows by a field, picking specific properties, flattening nested arrays, or sorting by multiple keys. Here are several practical patterns you can use immediately.

```
// Group orders by status
const byStatus = _.groupBy(getOrders.data, 'status');
// { pending: [...], completed: [...], cancelled: [...] }

// Pick only certain columns (useful before sending to a mutation)
const sanitized = _.map(getOrders.data, row =>
  _.pick(row, ['id', 'customer_name', 'total'])
);

// Sort by multiple fields
const sorted = _.orderBy(
  getOrders.data,
  ['status', 'created_at'],
  ['asc', 'desc']
);

// Get unique values from a column
const uniqueStatuses = _.uniq(_.map(getOrders.data, 'status'));

// Flatten nested arrays from a join
const flat = _.flatMap(getOrders.data, row => row.line_items || []);

return sorted;
```

**Expected result:** JS Query returns transformed data using lodash operations without any import configuration.

### 6. Use moment.js for date formatting and calculation

Moment.js is pre-bundled in Retool as moment without any import needed. Use it to format dates for display, calculate relative times (e.g., '3 days ago'), or compare dates. Note that for new projects, the moment.js team recommends using date-fns or Luxon, but moment works fine in Retool if you are already familiar with it.

```
// Format a date from query data for display
const rows = getData.data;
return rows.map(row => ({
  ...row,
  created_display: moment(row.created_at).format('MMM D, YYYY'),
  relative_time: moment(row.created_at).fromNow(), // '3 days ago'
  days_old: moment().diff(moment(row.created_at), 'days')
}));

// Check if a deadline has passed
const isOverdue = moment(table1.selectedRow.data?.due_date).isBefore(moment());
console.log('Is overdue:', isOverdue);

// Add 30 days to today (for default expiry)
const expiryDate = moment().add(30, 'days').format('YYYY-MM-DD');
```

**Expected result:** Dates in query results are formatted and compared using moment.js methods.

## Complete code example

File: `App Settings: Preloaded JavaScript + JS Query: dataProcessor`

```javascript
// === App Settings → Preloaded JavaScript ===
// <script src="https://cdn.jsdelivr.net/npm/papaparse@5.4.1/papaparse.min.js"></script>
// <script src="https://cdn.jsdelivr.net/npm/date-fns@3.6.0/cdn.min.js"></script>

// === JS Query: processOrdersReport ===
// Combines pre-bundled lodash + CDN-loaded date-fns + pre-bundled moment

const orders = getOrders.data;

if (!orders || orders.length === 0) {
  return [];
}

// Step 1: Format dates with date-fns (from CDN)
const withFormattedDates = orders.map(order => ({
  ...order,
  created_display: dateFns.format(new Date(order.created_at), 'MMM d, yyyy'),
  days_since_order: dateFns.differenceInDays(new Date(), new Date(order.created_at))
}));

// Step 2: Sort by days overdue using lodash
const sorted = _.orderBy(
  withFormattedDates,
  ['days_since_order'],
  ['desc']
);

// Step 3: Group by status
const grouped = _.groupBy(sorted, 'status');

// Step 4: Add summary stats per group
const summary = _.mapValues(grouped, (groupOrders, status) => ({
  status,
  count: groupOrders.length,
  total_value: _.sumBy(groupOrders, 'total_amount'),
  oldest_order_days: _.maxBy(groupOrders, 'days_since_order')?.days_since_order
}));

return {
  orders: sorted,
  summary: Object.values(summary)
};
```

## Common mistakes

- **Using require() or import statements in a Retool JS Query** — undefined Fix: Retool JS Queries run in a browser context, not Node.js. Use the global variable exported by the library's UMD build instead. Load libraries via Preloaded JavaScript and reference their global (e.g., _, moment, dateFns).
- **Adding a library to Preloaded JavaScript but getting 'X is not defined' errors** — undefined Fix: Reload the app after saving Preloaded JavaScript settings. Also check the library's global name — it may be different from the package name (e.g., Papa Parse exports as Papa, not papaparse).
- **Loading the same library twice (once pre-bundled, once from CDN)** — undefined Fix: Retool pre-bundles lodash, moment, and others. Adding them again from CDN creates two instances and can cause version conflicts. Remove the CDN import for pre-bundled libraries.
- **Using the minified source map URL instead of the production bundle** — undefined Fix: Use .min.js (not .js or .map) in your CDN URL. The source map (.map) file is for debugging, not execution.

## Best practices

- Always pin library versions in your CDN URLs (e.g., papaparse@5.4.1) — using 'latest' can break your app when the library releases a breaking change.
- Check Retool's pre-bundled library list before adding a CDN script — lodash, moment, Papa Parse, and others are already included.
- Load libraries from a reputable CDN with SRI (Subresource Integrity) hash for security — jsDelivr and unpkg both support SRI.
- Keep the number of Preloaded JavaScript libraries small — each library adds to initial load time for end users.
- For self-hosted Retool with strict CSP, host libraries on your own CDN rather than external CDN URLs.
- Prefer smaller, modern alternatives when possible (date-fns over moment, native Array methods over lodash for simple operations).
- Document which libraries are loaded and why in a comment at the top of the Preloaded JavaScript field.

## Frequently asked questions

### Does Retool include lodash by default, or do I need to import it?

Lodash is pre-bundled in Retool and available as _ in all JS Queries without any import configuration. You can use _.groupBy, _.orderBy, _.pick, and all other lodash methods immediately. You do not need to add a CDN script tag for lodash.

### Can I use ES modules (import/export) in Retool JS Queries?

No. Retool JS Queries run in a sandboxed browser context that does not support ES module syntax. All libraries must be loaded as UMD bundles via Preloaded JavaScript and accessed as global variables. Use require() or import is not available.

### How do I load a private or internal JavaScript library into Retool?

Host your library on an internal CDN or file server accessible from your Retool instance. Add the script tag with the internal URL to App Settings → Preloaded JavaScript. For Retool Cloud, the URL must be publicly accessible or served with appropriate CORS headers.

---

Source: https://www.rapidevelopers.com/retool-tutorial/how-to-import-libraries-in-retool-javascript-editor
© RapidDev — https://www.rapidevelopers.com/retool-tutorial/how-to-import-libraries-in-retool-javascript-editor
