# Project Structure and Configuration

Course: [Astro](https://www.devtutorials.ai/learn/astro) · Section: Get Started
Source: https://www.devtutorials.ai/learn/astro/get-started/project-structure

> Every folder and file in an Astro project, public/ vs src/, astro.config.mjs and defineConfig(), common options, integrations and astro add, using JavaScript and TypeScript together, astro check, editor setup and the dev toolbar.

## Project Structure and Configuration

- Every Astro project has the same basic folders and files.
- Each one has a clear job.
- When you know the jobs, you always know where a new file should go.
- In this lesson, you will meet every part of a project and change its settings.

**Simple lesson:** Astro decides a lot from where a file lives, so the right folder matters.

### A Real-World Example

- Asha's Dev Journal is growing.
- She now has a logo, a header used on every page and a small helper that formats dates.
- She wants to set her real web address, so links shared online are correct.
- Each of these things belongs in a different place in the project.

Press **▶ Run**. Then open the files in the explorer and see where each one lives.

<h1>Asha's Dev Journal</h1>
<p>Last updated: {formatDate(updated)}</p>
`,
    "src/components/Header.astro": `<header>
    <img src="/logo.svg" alt="Dev Journal logo" width="40" height="40" />
    <strong>Dev Journal</strong>
</header>
`,
    "src/lib/formatDate.ts": `export function formatDate(date: Date): string {
    return date.toDateString();
}
`,
    "public/logo.svg": `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40"><rect width="40" height="40" rx="9" fill="#7c3aed"/><text x="20" y="27" font-size="18" text-anchor="middle" fill="#fff" font-family="sans-serif">DJ</text></svg>
`,
  }}
/>

What happened:

- The page is in `src/pages`, so it became the home page.
- The header is in `src/components`, so you can reuse it on any page.
- The helper is a plain `.ts` file in `src/lib`.
- The logo is in `public`, and the page loads it as `/logo.svg`.

### What You Will Learn

In this lesson, you will learn:

1. The job of every folder in an Astro project.
2. The difference between `public/` and `src/`.
3. What `package.json`, `tsconfig.json` and the `.astro` folder do.
4. How to change settings in `astro.config.mjs`.
5. What integrations are and how to add one.
6. How to use JavaScript and TypeScript files together.
7. How to set up your editor and use the dev toolbar.

## The Folders of a Project

### src/pages

- **Pages folder** = `src/pages`. Every `.astro` file here becomes a page.
- It is the only folder Astro needs.
- Sub-folders make longer addresses: `src/pages/blog/first.astro` becomes `/blog/first`.

Press **▶ Run**, then open the page list in the address bar. Each file became one address.

<a href="/blog">Blog</a>
`,
    "src/pages/blog/index.astro": `<h1>All posts</h1>
<a href="/blog/first-post">My first post</a>
`,
    "src/pages/blog/first-post.astro": `<h1>My first post</h1>
<p>This file is src/pages/blog/first-post.astro.</p>
<a href="/blog">Back to all posts</a>
`,
  }}
/>

- `index.astro` in a folder is that folder's main page: `src/pages/blog/index.astro` is `/blog`.

### src/components

- **Component** = a reusable piece of a page, such as a header, a card or a button.
- Put components in `src/components`.
- Files here do **not** become pages. You import them into pages.

### src/layouts

- **Layout** = a component that gives pages a shared frame, such as the `<html>`, `<head>` and header.
- Put them in `src/layouts`. You will build one in **Layouts and the Document Head**.

### Other Folders in src

- `src/styles` is a common home for global CSS files.
- `src/lib` or `src/utils` is a common home for helper `.ts` or `.js` files.
- `src/content.config.ts` describes your content collections. You will add it in **Content Collections**.
- Only `src/pages` and `src/content.config.ts` have a fixed name. The other folder names are just good habits.

**Simple lesson:** pages go in `src/pages`, reusable pieces in `src/components`, frames in `src/layouts`.

## public vs src

Watch the animation. Files in `src/` go through Astro's build. Files in `public/` are copied as they are.

<figure className="ast-fig ngc-fig">
<svg viewBox="0 0 640 220" role="img" aria-labelledby="fig-public-src" style={{ width: "100%", maxWidth: 640, height: "auto" }}>
  <title id="fig-public-src">Animation: files from src go through astro build and come out changed. Files from public skip the build and are copied unchanged to dist.</title>
  <defs><marker id="ps-arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path d="M0 0 L10 5 L0 10 z" style={{ fill: "var(--ngc-slate)" }} /></marker></defs>
  <rect x="20" y="30" width="150" height="60" rx="10" style={{ fill: "var(--ngc-purple-soft)", stroke: "var(--ngc-purple)" }} />
  <text x="95" y="56" textAnchor="middle" style={{ fill: "var(--ngc-purple)", fontFamily: "var(--mono)", fontSize: 14, fontWeight: 700 }}>src/</text>
  <text x="95" y="76" textAnchor="middle" style={{ fill: "var(--ngc-purple)", fontFamily: "var(--sans)", fontSize: 11 }}>.astro, .ts, .css</text>
  <rect x="20" y="130" width="150" height="60" rx="10" style={{ fill: "var(--ngc-blue-soft)", stroke: "var(--ngc-blue)" }} />
  <text x="95" y="156" textAnchor="middle" style={{ fill: "var(--ngc-blue)", fontFamily: "var(--mono)", fontSize: 14, fontWeight: 700 }}>public/</text>
  <text x="95" y="176" textAnchor="middle" style={{ fill: "var(--ngc-blue)", fontFamily: "var(--sans)", fontSize: 11 }}>logo.svg, robots.txt</text>
  <rect x="245" y="30" width="150" height="60" rx="10" style={{ fill: "var(--ngc-orange)", stroke: "var(--ngc-orange)" }} />
  <text x="320" y="58" textAnchor="middle" style={{ fill: "#fff", fontFamily: "var(--sans)", fontSize: 14, fontWeight: 700 }}>astro build</text>
  <text x="320" y="77" textAnchor="middle" style={{ fill: "#fff", fontFamily: "var(--sans)", fontSize: 11 }}>compiles and shrinks</text>
  <text x="320" y="165" textAnchor="middle" style={{ fill: "var(--ngc-slate)", fontFamily: "var(--sans)", fontSize: 12 }}>copied as they are</text>
  <rect x="470" y="70" width="150" height="80" rx="10" style={{ fill: "var(--ngc-green-soft)", stroke: "var(--ngc-green)" }} />
  <text x="545" y="105" textAnchor="middle" style={{ fill: "var(--ngc-green)", fontFamily: "var(--mono)", fontSize: 14, fontWeight: 700 }}>dist/</text>
  <text x="545" y="125" textAnchor="middle" style={{ fill: "var(--ngc-green)", fontFamily: "var(--sans)", fontSize: 11 }}>the finished site</text>
  <path d="M170 60 H241" style={{ stroke: "var(--ngc-slate)", strokeWidth: 2, fill: "none" }} markerEnd="url(#ps-arrow)" />
  <path d="M395 60 L466 95" style={{ stroke: "var(--ngc-slate)", strokeWidth: 2, fill: "none" }} markerEnd="url(#ps-arrow)" />
  <path d="M170 160 H400 L466 125" style={{ stroke: "var(--ngc-slate)", strokeWidth: 2, fill: "none", strokeDasharray: "5 5" }} markerEnd="url(#ps-arrow)" />
  <circle r="6" style={{ fill: "var(--ngc-purple)" }}><animateMotion dur="3s" repeatCount="indefinite" path="M170 60 H395 L466 95" /></circle>
  <circle r="6" style={{ fill: "var(--ngc-blue)" }}><animateMotion dur="3s" begin="1.5s" repeatCount="indefinite" path="M170 160 H400 L466 125" /></circle>
</svg>
<figcaption>src files are built. public files are copied unchanged.</figcaption>
</figure>

### What Goes in public/

- **public/** = files copied to the finished site without any change.
- Use it for `favicon.svg`, `robots.txt`, fonts and files that must keep their exact name.
- A file at `public/logo.svg` is served at `/logo.svg`.
- Do **not** write `/public` in the path. The word `public` is not part of the address.

### What Goes in src/

- **src/** = your source code. Astro processes every file here.
- `.astro` and `.ts` files are compiled.
- CSS is combined and made smaller.
- Images in `src/` can be resized and optimised. You will learn this in **Images and Fonts**.

Ask one question: "Must this file stay exactly as it is?" If yes, put it in `public/`. If Astro should process it, put it in `src/`.

**Simple lesson:** `public/` is copied, `src/` is built.

## The Files at the Top

### package.json

- **package.json** = the project's ID card for npm.
- It lists the packages the project needs, such as `astro`.
- Its `scripts` give short names to commands: `dev`, `build` and `preview`.
- `npm run dev` looks up `dev` here and runs `astro dev`.

### tsconfig.json

- **tsconfig.json** = settings for TypeScript and for your editor.
- Every Astro project has one, even a JavaScript project.
- It starts with `"extends": "astro/tsconfigs/strict"`. That means "use Astro's strict settings".
- Your editor reads it to give you hints and warnings.

### The .astro Folder

- Astro creates a hidden `.astro` folder when you run it.
- It holds **generated types**: files Astro writes for your editor.
- Never edit it. Astro writes it again whenever it needs to.
- It is listed in `.gitignore`, so Git does not save it.

### node_modules and dist

- `node_modules/` holds the downloaded packages. `npm install` fills it.
- `dist/` holds the built site. `npm run build` fills it.
- Never edit either one by hand. Both are made by commands.

**Simple lesson:** you edit `src/`, `public/` and the config files. Commands make everything else.

## Configuring Astro

### astro.config.mjs and defineConfig()

- **astro.config.mjs** = Astro's settings file. It sits at the top of the project.
- A new project's file is almost empty.
- You list your settings inside `defineConfig({ ... })`.
- `defineConfig()` changes nothing by itself. It lets your editor suggest the right option names.

```js
// @ts-check
import { defineConfig } from "astro/config";

export default defineConfig({
  site: "https://asha.dev",
  server: { port: 3000 },
});
```

- `// @ts-check` asks your editor to check this file, even though it is JavaScript.

### Common Options

- `site` = your site's real web address. Astro uses it to build full links.
- `base` = a sub-folder the site lives in, such as `/journal`, when it is not at the root of the domain.
- `integrations` = a list of add-ons. More on these below.
- `server` = dev server settings, such as `port`.
- `vite` = settings passed to **Vite**, the build tool Astro uses inside.

Change `site` in the config below and press **▶ Run**. The page reads it with `Astro.site`.

<p>My site: {site?.href ?? "no site set"}</p>
<p>Full link: <code>{fullLink.href}</code></p>
`,
  }}
/>

- **Without `site`**, Astro cannot know your domain. Full links, sitemaps and RSS feeds need it.
- **`Astro.site`** is a `URL` object, or `undefined` when `site` is not set.

### TypeScript Config File

- **[TS]** You can name the file `astro.config.ts` instead. It works the same way.
- **[JS]** Keep `astro.config.mjs`. The `// @ts-check` line still gives you hints.

### Finding Every Option

- The **Configuration Reference** in the Astro docs lists every option.
- Each entry shows the type, the default value and an example.
- Look there before you search a blog. The docs match your Astro version.

**Simple lesson:** settings live in `defineConfig()`. Set `site` early, because many features need it.

## Working with Integrations

### What Integrations Are

- **Integration** = an add-on package that gives Astro a new skill.
- Examples:
  - `@astrojs/react` lets you use React components.
  - `@astrojs/mdx` lets you write pages in MDX.
  - `@astrojs/sitemap` makes a sitemap file for search engines.
- An integration is added to the `integrations` list in the config.

### Adding an Integration with astro add

The easy way is one command:

```bash
npx astro add sitemap
```

It does three things for you:

1. Installs the package with npm.
2. Imports it at the top of `astro.config.mjs`.
3. Adds `sitemap()` to the `integrations` list.

It shows you the change and asks before it saves it.

### Manual Installation

You can do the same steps by hand:

```bash
npm install @astrojs/sitemap
```

Then edit the config:

```js
import { defineConfig } from "astro/config";
import sitemap from "@astrojs/sitemap";

export default defineConfig({
  site: "https://asha.dev",
  integrations: [sitemap()],
});
```

### Integration Options

- Many integrations take options inside the brackets.
- Example: `sitemap({ filter: (page) => !page.includes("/drafts/") })` leaves drafts out of the sitemap.
- Each integration's page in the docs lists its options.

Press **▶ Run**. The terminal lists the integrations that the config adds.

<a href="/about">About</a>
`,
    "src/pages/about.astro": `<h1>About</h1>
<a href="/">Home</a>
`,
  }}
/>

- On your computer, `npm run build` would now also write `sitemap-index.xml` to `dist/`.
- In this lab, integrations are named in the terminal, but they do not run. The lab cannot install packages.

### Official Integrations

The Astro team keeps these, and many more:

- **UI frameworks:** React, Preact, Vue, Svelte, Solid, Alpine.js.
- **Adapters:** Node, Vercel, Netlify, Cloudflare. You meet them in **On-Demand and Mixed Rendering**.
- **Other:** MDX, Markdoc, sitemap, partytown.

Tailwind CSS is no longer an integration. `npx astro add tailwind` adds it as a Vite plugin instead. You will add it in **Styling**.

**Simple lesson:** use `npx astro add <name>`. It installs the package and edits the config for you.

## Using JavaScript and TypeScript Together

### Mixing .js and .ts Files

- One project can hold both `.js` and `.ts` files.
- An `.astro` file can import either one.
- A `.ts` file can import a `.js` file, and the other way round.
- You can start in JavaScript and add types later, one file at a time.

The lab below has one `.js` helper and one `.ts` helper. The page uses both.

<p>{post}</p>
<p>Reading time: {readingTime(post)} min</p>
`,
    "src/lib/greet.js": `export function greet(name) {
    return "Welcome back, " + name + "!";
}
`,
    "src/lib/readingTime.ts": `export function readingTime(text: string): number {
    const words = text.split(" ").length;
    return Math.max(1, Math.round(words / 200));
}
`,
  }}
/>

- `greet.js` is plain JavaScript.
- `readingTime.ts` says its input is a `string` and its result is a `number`.
- Astro removes the types when it builds. The browser never sees them.

### [JS] Editor Hints Without TypeScript

- Your editor still reads `tsconfig.json`, so it can warn about some mistakes in `.js` files.
- You can add **JSDoc** comments to describe values without writing TypeScript:

```js
/**
 * @param {string} name
 * @returns {string}
 */
export function greet(name) {
  return "Welcome back, " + name + "!";
}
```

- **JSDoc** = comments that start with `/**` and describe a function's inputs and output.
- It is optional. Add it when a function is used in many places.

### [TS] Checking Types with astro check

- `astro dev` and `astro build` do **not** check your types. They only remove them.
- A type mistake can still reach your site.
- Run `npx astro check` to check every `.astro` and `.ts` file.
- The first time, it offers to install `@astrojs/check` and `typescript`. Say yes.
- Add it to your build script so mistakes stop the build:

```json
"build": "astro check && astro build"
```

A site that builds is not proof that its types are right. Only `astro check` checks them.

**Simple lesson:** mix `.js` and `.ts` freely. If you use types, run `astro check`.

## Editor Setup and the Dev Toolbar

### The Astro VS Code Extension

- Install **Astro** from the VS Code Extensions panel. It is published by the Astro team.
- It adds colours (syntax highlighting) for `.astro` files.
- It adds **IntelliSense**: suggestions, errors and "go to definition" as you type.
- Other editors, such as WebStorm, Zed and Neovim, have Astro support too.

### The Prettier Plugin

- **Prettier** = a tool that formats your code neatly.
- It needs the `prettier-plugin-astro` package to understand `.astro` files.

```bash
npm install --save-dev prettier prettier-plugin-astro
```

### The Dev Toolbar

- When `npm run dev` is running, a small toolbar appears at the bottom of your page.
- It only shows in development. Visitors never see it.
- Its built-in apps:
  - **Inspect** highlights the interactive islands on the page.
  - **Audit** checks for common accessibility and speed problems.
  - **Settings** lets you hide or move the toolbar.
- Turn it off for the whole project in the config: `devToolbar: { enabled: false }`.

**Simple lesson:** install the Astro extension first. Use the dev toolbar's Audit to catch problems early.

## TL;DR

1. **src/pages** = every `.astro` file here becomes a page.
2. **src/components** = reusable pieces. **src/layouts** = page frames.
3. **public/** = copied as it is. Served from the site root, without `/public`.
4. **package.json** = packages and scripts. **tsconfig.json** = editor and TypeScript settings.
5. **.astro/**, **node_modules/**, **dist/** = made by commands. Never edit them.
6. **astro.config.mjs** = settings inside `defineConfig()`:
   - `site` for full links,
   - `base` for a sub-folder,
   - `integrations` for add-ons,
   - `server.port` for the dev port.
7. **Integration** = add-on. Add it with `npx astro add <name>`.
8. **astro check** = the only command that checks your types.

## Common mistakes

<div className="tscroll">

| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
| Writing `/public/logo.svg` | The folder name is part of the path | Use `/logo.svg` | The image does not load |
| Putting a component in `src/pages` | Any folder works for components | Components go in `src/components` | It becomes a page of its own |
| Editing files in `.astro/` or `dist/` | They are normal source files | Edit `src/`, then run the command again | Your change is lost on the next run |
| Forgetting `site` | Astro knows the domain | Set `site` in the config | Full links, sitemap and RSS are wrong or missing |
| Adding an integration to the config only | Importing is enough | Install the package too, or use `astro add` | The build says it cannot find the package |
| Trusting `astro build` for types | A build checks types | Run `astro check` | Type mistakes reach the live site |

</div>

## Check Yourself

## Exercises

## What to Remember

- Pages in `src/pages`, components in `src/components`, layouts in `src/layouts`.
- `public/` is copied as it is and served from the root.
- Settings go in `astro.config.mjs` inside `defineConfig()`.
- Set `site` early. Add integrations with `npx astro add`.
- `.js` and `.ts` files work together. Only `astro check` checks types.

Next: **Component Basics**, where you build Asha's Header, Footer and PostCard.
