Command Palette

Search for a command to run...

Hectal
PHASE 2Beginner ~23 min· topic 2 of 7

Topic 2.2

Components in Files: Import and Export

In one line

Real React apps keep components in separate files and connect them with JavaScript modules: a file shares a component with export (default or named) and another file brings it in with the matching import.

Think of it like this

A school library. Each book sits on its own shelf with a label on the spine. When you need one, you don't copy it out by hand; you ask for it by its label and borrow it. **Files are shelves, export puts a label on what a file lends out, and import borrows it by that label.** Ask with the wrong label and you get nothing back.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

Module
A JavaScript file with its own private scope that shares things using export.
export
Marks a value (like a component) as available to other files.
import
Brings an exported value from another file or package into this one.
Default export
The one main export of a file. Imported without braces, under any name you choose.
Named export
An export with a fixed name. A file can have many. Imported with braces and the same name.
Import path
Where to find the module: ./Avatar.jsx (a file next to this one) or react (a package).
Package
Code installed with npm into node_modules, imported by its name, like react.
Feature folder
A folder that keeps all files for one feature (components, styles, helpers) together.

Step by step

01Start with everything in one file

A new app often starts with every component in App.jsx. That's fine for a few components, but soon the file is hundreds of lines long and hard to navigate.

src/App.jsxwhole filejsx
function Avatar() {
  return <img className="avatar" src="/asha.svg" alt="Asha" />;
}

function Gallery() {
  return (
    <section>
      <Avatar />
      <Avatar />
    </section>
  );
}

export default function App() {
  return <Gallery />;
}

02Plan the folder

Give each component its own file in src/components/. main.jsx and App.jsx stay at the top of src/. Helpers that aren't components (formatting, constants) go in their own small modules.

Folderwhole filetext
my-app/
├── index.html
├── package.json
└── src/
    ├── main.jsx            creates the root, renders <App />
    ├── App.jsx             the top component
    ├── components/
    │   ├── Avatar.jsx      export default function Avatar
    │   └── Gallery.jsx     export default function Gallery
    └── utils/
        └── format.js       export function formatName, export const SIZE
Plan the folderdiagram
Rendering diagram…

03Export from the component's file

Move Avatar into Avatar.jsx and add export default. Put a named export next to it if other files need a constant too.

src/components/Avatar.jsxwhole filejsx
import { SIZE } from "../utils/format.js";

export default function Avatar() {
  return <img className="avatar" src="/asha.svg" alt="Asha" width={SIZE} height={SIZE} />;
}

04Import where you use it

Gallery.jsx imports the default export with no braces. The path starts with ./ because both files are in the same folder.

src/components/Gallery.jsxwhole filejsx
import Avatar from "./Avatar.jsx";

export default function Gallery() {
  return (
    <section>
      <Avatar />
      <Avatar />
    </section>
  );
}

05Use named exports for several things

A utility file usually has several named exports. Import exactly the names you need, in braces.

src/utils/format.jswhole filejavascript
export const SIZE = 48;

export function formatName(first, last) {
  return `${first} ${last}`.trim();
}

// elsewhere:
// import { SIZE, formatName } from "../utils/format.js";

06Match the import to the export

The rule fits in a table. Getting it backwards is the bug in the "break it" section below.

Cheat sheetwhole filetext
Export                                 Import
-------------------------------------  ----------------------------------------
export default function Avatar() {}    import Avatar from "./Avatar.jsx";
                                       (any name works: import Pic from ...)
export function Avatar() {}            import { Avatar } from "./Avatar.jsx";
                                       (rename: import { Avatar as Pic } ...)
both in one file                       import Avatar, { SIZE } from "./Avatar.jsx";
everything named                       import * as Things from "./Avatar.jsx";

Try it yourself

  1. 1

    Fix the import

    In "What an import really reads", change const Wrong = avatarModule.Avatar; to read the default export instead. Predict the console, then run it. (No error, and the Avatar appears twice.)

    Now pretend Avatar.jsx used a named export: change the module to { Avatar: Avatar }. Which of the two lines now reads it correctly?

  2. 2

    Add a file

    In "Three files, shown as one", add a new section components/Title.jsx with a Title component that returns <h2>Our team</h2>, and render it above the avatars in Gallery. Write the import and export comments it would need in a real project.

  3. 3

    Rename on import

    In the third example, change the first line to import React, { useState as useMemory } from "react"; and fix the component to use useMemory. Does it still work? (Yes: as only renames the local variable.)

Code & diagrams

Three files, shown as one New tab

A live example can only be one file, so the file boundaries are marked with comments. The commented `import`/`export` lines are what each real file would contain.

Preview

Press Run to see the page.

Console

Nothing logged yet.
What an import really reads New tab

A module behaves like an object of exports, and the default export is stored under the name `default`. Asking for a name that isn't there gives `undefined`, and rendering `undefined` as a component is React's "Element type is invalid" error. This example throws on purpose.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

default import is a function
named import is undefined
Uncaught Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.

Check the render method of `App`.
An error occurred in the <main> component.

Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://react.dev/link/error-boundaries to learn more about error boundaries.
Default, named and namespace imports from React itself New tab

`useState` imported by name and `React.useState` are the same function. You'll usually see named imports (`import { useState } from "react"`); `import React from "react"` is no longer needed for JSX since the new JSX transform (React 17).

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

React.useState === useState: true
createRoot is a function
React version 19.2.8
The real error in a Vite project (browser console)text
// Avatar.jsx:  export default function Avatar() { ... }
// Gallery.jsx: import { Avatar } from "./Avatar.jsx";   <- braces: asks for a NAMED export

Uncaught SyntaxError: The requested module '/src/components/Avatar.jsx' does not provide an export named 'Avatar'

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Import a default export with braces

In a Vite project, Avatar.jsx has export default function Avatar(). In Gallery.jsx, write import { Avatar } from "./Avatar.jsx";.

terminal
$ Browser console
── what you'll see ──
Uncaught SyntaxError: The requested module '/src/components/Avatar.jsx' does not provide an export named 'Avatar'

Break #2

Forget to export the component

Delete the word export from Avatar.jsx but keep the import in Gallery.jsx.

terminal
$ Browser console
── what you'll see ──
Uncaught SyntaxError: The requested module '/src/components/Avatar.jsx' does not provide an export named 'default'

Myth vs fact

Myth

Default exports are faster or better for React.

Fact

React doesn't care. It's a code-style choice. Named exports keep names consistent across files and work well with editor refactoring; default exports are common for one-component files and required by some frameworks for pages (for example Next.js page.jsx).

Myth

You must import React from "react" in every file that has JSX.

Fact

Not since the new JSX transform (React 17, used by Vite and Next.js). The compiler imports the JSX helper for you. You only import what you use, like useState.

Myth

One component per file is a rule.

Fact

It's a convention for components other files use. Small private helpers can live in the same file without export.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    ES module imports are live bindings, resolved and checked when modules are linked, before any code runs. That's why a missing named export is a SyntaxError that stops the page, not a runtime undefined. Bundlers that convert CommonJS (like React's own package) add interop, which is why import React from "react" works even though React has no real ES default export.

  • ▸

    Fast Refresh (Vite's React plugin, Next.js) keeps component state while you edit a file only if the file exports only components. Exporting a constant or helper from a component file makes edits fall back to a full reload; the react-refresh/only-export-components ESLint rule warns about it.

  • ▸

    Avoid circular imports (A imports B, B imports A). They can leave one side undefined at the moment it's used. If two components need each other, the shared part usually belongs in a third file.

  • ▸

    Barrel files (components/index.js re-exporting everything) make imports shorter but can slow builds and tests and pull unused code into bundles. Topic 22.1 weighs the trade-off.

Remember this

  1. 1

    A module is a JavaScript file that keeps its variables to itself unless it exports them. Another file imports what it needs. This is plain JavaScript (ES modules, taught in /javascript), not something React invented. React projects use it to give each component its own file, so Gallery.jsx can use Avatar from Avatar.jsx without both living in one giant file.

  2. 2

    There are two kinds of export. A default export is the file's main thing: export default function Avatar() {}. A file can have at most one. You import it without braces and may give it any name: import Avatar from "./Avatar.jsx" (or import Pic from ..., which is the same function). A named export is labelled with its own name: export function Avatar() {} or export const SIZE = 48. A file can have many. You import them with braces and the exact same name: import { Avatar, SIZE } from "./Avatar.jsx"; rename on the way in with as: import { Avatar as Pic } from ....

  3. 3

    Mixing them up is the most common import bug. import { Avatar } from "./Avatar.jsx" asks for a named export called Avatar; if the file only has export default, there is no such name. In the browser this stops the whole page with does not provide an export named 'Avatar'. If the mix-up reaches React some other way, Avatar is undefined and React throws Element type is invalid: … got: undefined, and even suggests you "mixed up default and named imports".

  4. 4

    The usual convention is one component per file, with the file named after the component: Avatar.jsx holds Avatar. Many teams use a default export for that main component; others prefer named exports everywhere because names then stay the same across the codebase and editors find and rename them more reliably. Both are fine; be consistent. Small helper components used by only one file can stay in that file and simply not be exported, which keeps them private.

  5. 5

    Import paths: ./ means "in this folder", ../ means "one folder up". Paths without ./ (like "react") are packages from node_modules. Files containing JSX usually end in .jsx (.tsx with TypeScript, Phase 12). Vite lets you leave off .js/.jsx in import paths, but writing the extension is never wrong. The live examples on this site are a single file each, so in them all components sit together; the non-live blocks show how they would be split.

  6. 6

    Folder layout: a small Vite app keeps main.jsx (creates the root) and App.jsx in src/, with components in src/components/. As the app grows, teams group files by feature (src/features/cart/…) so everything about one feature lives together. Topic 22.1 covers layouts for large apps. Start simple and reorganise when a folder becomes hard to scan.

Explain it without notes

01

What is the difference between a default export and a named export? How do you import each?

02

A teammate gets Element type is invalid … got: undefined. What are the likely causes?

03

Why do React projects usually put one component per file?

04

What does ./, ../ and a bare name like react mean in an import path?

Practice

01

Button.jsx contains export function Button() {...} and export const SIZES = ["sm", "md"]. Write the one import line that brings both into App.jsx (same folder).

02

Split this into Card.jsx (default export) and App.jsx: ``jsx function Card() { return <div className="card">Hello</div>; } export default function App() { return <Card />; }

Trade-offs

  • ↔

    Default exports: short imports and one obvious main thing per file, but the name can drift (import Pic from './Avatar') and auto-import tools are less reliable. Named exports: consistent names and easy refactoring, at the cost of braces everywhere.

  • ↔

    Many small files make each one easy to read but add navigation and import lines; very large files are the opposite. Feature folders keep related files close as the app grows.

Done when you can

  • I can export a component as default or named, and import it the matching way.

  • I can read and write import paths with ./, ../ and package names.

  • I can lay out a small Vite app's src/ folder.

  • I can recognise the errors caused by a wrong or missing export.

  • I know when to keep a helper component private in the same file.