# Read data from Airtable

Reading data from your Airtable base is one of the most common things you’ll want your extension to do. The Interface Extensions SDK makes it easy to fetch tables, fields, and records—and keep your UI in sync as data changes.

## Use [useBase()](https://airtable.com/developers/interface-extensions/api/ui/hooks/usebase.md) to access your base

Every extension is scoped to a single Airtable base. You can grab the base model like this:

```
import {useBase} from '@airtable/blocks/interface/ui';

// renders a list of tables and automatically updates
function TableList() {
     const base = useBase(); // access base schema

     const tables = base.tables.map(table => {
         return <li key={table.id}>{table.name}</li>;
     });

     return <ul>{tables}</ul>;
}
```

## Get a table

Once you have the base, you can access tables by name or ID:

```
const table = base.getTableByName('Tasks');
```

## Read records with [useRecords()](https://airtable.com/developers/interface-extensions/api/ui/hooks/userecords.md)

This hook allows you to work with the records in a particular table. It automatically loads data, listens for changes, and re-renders when records update.

```
 import {useBase, useRecords} from '@airtable/blocks/interface/ui';

 function RecordList() {
     const base = useBase();
     const table = base.tables[0];

     // grab all the records available to the extension
     const records = useRecords(table);

     // render a list of records:
     return (
         <ul>
             {records.map(record => {
                 return <li key={record.id}>{record.name}</li>;
             })}
         </ul>
     );
 }
```

## Read cell values with [getCellValue()](https://airtable.com/developers/interface-extensions/api/models/record.md#getCellValue)

Each record contains a getCellValue() method that you can use to read individual field values:

```
const taskName = record.getCellValue('Name');
const status = record.getCellValue('Status');
```

## Read data from multiple tables

Interface Extensions can read data from multiple tables in a base simultaneously, providing access to those tables have been configured in Interface Designer.

### Defining tables to read data from with custom properties

The recommended way to work with tables in your extension code is to use [custom properties](https://airtable.com/developers/interface-extensions/guides/builders-custom-properties.md) to let builders configure (in Interface Designer) which tables to read data from:

```js
import {useCustomProperties} from '@airtable/blocks/interface/ui';

function getCustomProperties(base) {
    return [
        {
            key: 'milestonesTable',
            label: 'Project Milestones Table',
            type: 'table',
            defaultValue: base.tables.find((table) => table.name.toLowerCase().includes('milestones')),
        },
        {
            key: 'companyHolidaysTable',
            label: 'Company Holidays Table',
            type: 'table',
            defaultValue: base.tables.find((table) => table.name.toLowerCase().includes('holidays')),
        },
    ];
}

function MyExtension() {
    const {customPropertyValueByKey} = useCustomProperties(getCustomProperties);
    const milestonesTable = customPropertyValueByKey.milestonesTable;
    const companyHolidaysTable = customPropertyValueByKey.companyHolidaysTable;

    // Now you can read data from both tables...
}
```

### Reading data from multiple tables with [useRecords()](https://airtable.com/developers/interface-extensions/api/ui/hooks/userecords.md)

Once you have references to the tables you intend to read data from, you can read records from those tables by using [useRecords()](https://airtable.com/developers/interface-extensions/api/ui/hooks/userecords.md) on each table. In the following example, the extension displays key dates in the next week across two separate (but loosely related) tables that both contain date fields:

```js
import {useCustomProperties, useRecords} from '@airtable/blocks/interface/ui';

export default function KeyDatesNextWeek() {
  const {customPropertyValueByKey} = useCustomProperties(getCustomProperties);
  const milestonesTable = customPropertyValueByKey.milestonesTable; // e.g., Project Milestones
  const companyHolidaysTable = customPropertyValueByKey.companyHolidaysTable;    // e.g., Company Holidays

  const milestoneRecords = useRecords(milestonesTable);
  const holidayRecords   = useRecords(companyHolidaysTable);

  const now = new Date();
  const aWeekFromNow = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000);

  const keyDates = [
    // Assumes "Milestone Date" is a date field on the Milestones table
    ...milestoneRecords.map(r => ({
      kind: 'Project Milestone',
      name: r.name ?? r.id,
      date: r.getCellValue('Milestone Date'),
    })),
    // Assumes "Holiday Date" is a date field on the tasks/holidays table
    ...holidayRecords.map(r => ({
      kind: 'Company Holiday',
      name: r.name ?? r.id,
      date: r.getCellValue('Holiday Date'),
    })),
  ]
  .filter(x => x.date >= now && x.date < aWeekFromNow)
  .sort((a, b) => a.date - b.date);

  return (
    <div>
      <h3>Key dates (next 7 days)</h3>
      {keyDates.length === 0 ? (
        <div>No upcoming key dates.</div>
      ) : (
        <ul>
          {keyDates.map((x, i) => (
            <li key={i}>
              <strong>{x.kind}</strong>: {x.name} — {x.date.toDateString()}
            </li>
          ))}
        </ul>
      )}
    </div>
  );
}
```

## For more details, check the API docs

> 👉 See the full [Interface Extensions API documentation](https://airtable.com/developers/interface-extensions/api)
