diff options
Diffstat (limited to 'docs/src/pages/en/guides')
-rw-r--r-- | docs/src/pages/en/guides/aliases.md | 47 | ||||
-rw-r--r-- | docs/src/pages/en/guides/data-fetching.md | 56 | ||||
-rw-r--r-- | docs/src/pages/en/guides/debugging.md | 7 | ||||
-rw-r--r-- | docs/src/pages/en/guides/deploy.md | 528 | ||||
-rw-r--r-- | docs/src/pages/en/guides/environment-variables.md | 48 | ||||
-rw-r--r-- | docs/src/pages/en/guides/imports.md | 142 | ||||
-rw-r--r-- | docs/src/pages/en/guides/markdown-content.md | 332 | ||||
-rw-r--r-- | docs/src/pages/en/guides/pagination.md | 108 | ||||
-rw-r--r-- | docs/src/pages/en/guides/publish-to-npm.md | 216 | ||||
-rw-r--r-- | docs/src/pages/en/guides/rss.md | 53 | ||||
-rw-r--r-- | docs/src/pages/en/guides/styling.md | 640 |
11 files changed, 0 insertions, 2177 deletions
diff --git a/docs/src/pages/en/guides/aliases.md b/docs/src/pages/en/guides/aliases.md deleted file mode 100644 index 6c226a5a6..000000000 --- a/docs/src/pages/en/guides/aliases.md +++ /dev/null @@ -1,47 +0,0 @@ ---- -layout: ~/layouts/MainLayout.astro -title: Aliases -description: An intro to aliases with Astro. ---- - -An **alias** is a way to create shortcuts for your imports. - -Aliases can help improve the development experience in codebases with many directories or relative imports. - -```astro ---- -// my-project/src/pages/about/company.astro - -import Button from '../../components/controls/Button.astro'; -import logoUrl from '../../assets/logo.png?url'; ---- -``` - -In this example, a developer would need to understand the tree relationship between `src/pages/about/company.astro`, `src/components/controls/Button.astro`, and `src/assets/logo.png`. And then, if the `company.astro` file were to be moved, these imports would also need to be updated. - -You can add import aliases from either `tsconfig.json` or `jsconfig.json`. - -```json -{ - "compilerOptions": { - "baseUrl": ".", - "paths": { - "asset:*": ["src/assets/*?url"], - "component:*": ["src/components/*.astro"] - } - } -} -``` - -With this change, you can now import using the aliases anywhere in your project: - -```astro ---- -// my-project/src/pages/about/company.astro - -import Button from 'component:Button'; -import logoUrl from 'asset:logo.png'; ---- -``` - -These aliases are also integrated automatically into [VSCode](https://code.visualstudio.com/docs/languages/jsconfig) and other editors. diff --git a/docs/src/pages/en/guides/data-fetching.md b/docs/src/pages/en/guides/data-fetching.md deleted file mode 100644 index a0481f059..000000000 --- a/docs/src/pages/en/guides/data-fetching.md +++ /dev/null @@ -1,56 +0,0 @@ ---- -layout: ~/layouts/MainLayout.astro -title: Data Fetching -description: Learn how to fetch remote data with Astro using the fetch API. ---- - -Astro components and pages can fetch remote data to help generate your pages. Astro provides two different tools to pages to help you do this: **fetch()** and **top-level await.** - -## `fetch()` - -Astro pages have access to the global `fetch()` function in their setup script. `fetch()` is a native JavaScript API ([MDN<span class="sr-only">- fetch</span>](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch)) that lets you make HTTP requests for things like APIs and resources. - -Even though Astro component scripts run inside of Node.js (and not in the browser) Astro provides this native API so that you can fetch data at page build time. - -```astro ---- -// Movies.astro -const response = await fetch('https://example.com/movies.json'); -const data = await response.json(); -// Remember: Astro component scripts log to the CLI -console.log(data); ---- -<!-- Output the result to the page --> -<div>{JSON.stringify(data)}</div> -``` - -## Top-level await - -`await` is another native JavaScript feature that lets you await the response of some asynchronous promise ([MDN<span class="sr-only">- await</span>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await)). Astro supports `await` in the top-level of your component script. - -**Important:** These are not yet available inside of non-page Astro components. Instead, do all of your data loading inside of your pages, and then pass them to your components as props. - -## Using `fetch()` outside of Astro Components - -If you want to use `fetch()` in a non-astro component, it is also globally available: - -```tsx -// Movies.tsx -import type { FunctionalComponent } from 'preact'; -import { h } from 'preact'; - -const data = fetch('https://example.com/movies.json').then((response) => - response.json() -); - -// Components that are build-time rendered also log to the CLI. -// If you loaded this component with a directive, it would log to the browser console. -console.log(data); - -const Movies: FunctionalComponent = () => { - // Output the result to the page - return <div>{JSON.stringify(data)}</div>; -}; - -export default Movies; -``` diff --git a/docs/src/pages/en/guides/debugging.md b/docs/src/pages/en/guides/debugging.md deleted file mode 100644 index 7e3211656..000000000 --- a/docs/src/pages/en/guides/debugging.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -layout: ~/layouts/MainLayout.astro -title: Debugging -description: Debug in Astro using the Debug component ---- - -Astro runs on the server and logs directly to your terminal, so it can be difficult to debug values from Astro. Astro's built-in `<Debug>` component can help you inspect values inside your files on the clientside. Read more about the [built-in Debug Component](/en/reference/builtin-components#debug-). diff --git a/docs/src/pages/en/guides/deploy.md b/docs/src/pages/en/guides/deploy.md deleted file mode 100644 index d5b6797db..000000000 --- a/docs/src/pages/en/guides/deploy.md +++ /dev/null @@ -1,528 +0,0 @@ ---- -layout: ~/layouts/MainLayout.astro -title: Deploy a Website -description: Multiple different methods to deploy a website with Astro. ---- - -The following guides are based on some shared assumptions: - -- You are using the default build output location (`dist/`). This location [can be changed using the `dist` configuration option](/en/reference/configuration-reference). -- You are using npm. You can use equivalent commands to run the scripts if you are using Yarn or other package managers. -- Astro is installed as a local dev dependency in your project, and you have set up the following npm scripts: - -```json -{ - "scripts": { - "start": "astro dev", - "build": "astro build", - "preview": "astro preview" - } -} -``` - -## Building The App - -You may run `npm run build` command to build the app. - -```bash -$ npm run build -``` - -By default, the build output will be placed at `dist/`. You may deploy this `dist/` folder to any of your preferred platforms. - -## GitHub Pages - -> **Warning:** By default, Github Pages will break the `_astro/` directory of your deployed website. To disable this behavior and fix this issue, make sure that you use the `deploy.sh` script below or manually add an empty `.nojekyll` file to your `public/` site directory. - -1. Set the correct `buildOptions.site` in `astro.config.mjs`. -1. Inside your project, create `deploy.sh` with the following content (uncommenting the appropriate lines), and run it to deploy: - - ```bash - #!/usr/bin/env sh - - # abort on errors - set -e - - # build - npm run build - - # navigate into the build output directory - cd dist - - # add .nojekyll to bypass GitHub Page's default behavior - touch .nojekyll - - # if you are deploying to a custom domain - # echo 'www.example.com' > CNAME - - git init - git add -A - git commit -m 'deploy' - - # if you are deploying to https://<USERNAME>.github.io - # git push -f git@github.com:<USERNAME>/<USERNAME>.github.io.git main - - # if you are deploying to https://<USERNAME>.github.io/<REPO> - # git push -f git@github.com:<USERNAME>/<REPO>.git main:gh-pages - - cd - - ``` - - > You can also run the above script in your CI setup to enable automatic deployment on each push. - -### GitHub Actions - -1. In the astro project repo, create `gh-pages` branch then go to Settings > Pages and set to `gh-pages` branch for GitHub Pages and set directory to `/` (root). -2. Set the correct `buildOptions.site` in `astro.config.mjs`. -3. Create the file `.github/workflows/main.yml` and add in the yaml below. Make sure to edit in your own details. -4. In GitHub go to Settings > Developer settings > Personal Access tokens. Generate a new token with repo permissions. -5. In the astro project repo (not \<YOUR USERNAME\>.github.io) go to Settings > Secrets and add your new personal access token with the name `API_TOKEN_GITHUB`. -6. When you push changes to the astro project repo CI will deploy them to \<YOUR USERNAME\>.github.io for you. - -```yaml -# Workflow to build and deploy to your GitHub Pages repo. - -# Edit your project details here. -# Remember to add API_TOKEN_GITHUB in repo Settings > Secrets as well! -env: - githubEmail: <YOUR GITHUB EMAIL ADDRESS> - deployToRepo: <NAME OF REPO TO DEPLOY TO (E.G. <YOUR USERNAME>.github.io)> - -name: Github Pages Astro CI - -on: - # Triggers the workflow on push and pull request events but only for the main branch - push: - branches: [main] - pull_request: - branches: [main] - - # Allows you to run this workflow manually from the Actions tab. - workflow_dispatch: - -jobs: - deploy: - runs-on: ubuntu-latest - - steps: - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v2 - - # Install dependencies with npm - - name: Install dependencies - run: npm ci - - # Build the project and add .nojekyll file to supress default behaviour - - name: Build - run: | - npm run build - touch ./dist/.nojekyll - - # Push to your pages repo - - name: Push to pages repo - uses: cpina/github-action-push-to-another-repository@main - env: - API_TOKEN_GITHUB: ${{ secrets.API_TOKEN_GITHUB }} - with: - source-directory: 'dist' - destination-github-username: ${{ github.actor }} - destination-repository-name: ${{ env.deployToRepo }} - user-email: ${{ env.githubEmail }} - commit-message: Deploy ORIGIN_COMMIT - target-branch: gh-pages -``` - -### Travis CI - -1. Set the correct `buildOptions.site` in `astro.config.mjs`. -2. Create a file named `.travis.yml` in the root of your project. -3. Run `npm install` locally and commit the generated lockfile (`package-lock.json`). -4. Use the GitHub Pages deploy provider template, and follow the [Travis CI documentation](https://docs.travis-ci.com/user/deployment/pages/). - - ```yaml - language: node_js - node_js: - - lts/* - install: - - npm ci - script: - - npm run build - deploy: - provider: pages - skip_cleanup: true - local_dir: dist - # A token generated on GitHub allowing Travis to push code on you repository. - # Set in the Travis settings page of your repository, as a secure variable. - github_token: $GITHUB_TOKEN - keep_history: true - on: - branch: master - ``` - -## GitLab Pages - -1. Set the correct `buildOptions.site` in `astro.config.mjs`. -2. Set `dist` in `astro.config.mjs` to `public` and `public` in `astro.config.mjs` to a newly named folder that is holding everything currently in `public`. The reasoning is because `public` is a second source folder in astro, so if you would like to output to `public` you'll need to pull public assets from a different folder. Your `astro.config.mjs` might end up looking like this: - - ```js - export default /** @type {import('astro').AstroUserConfig} */ ({ - // Enable the Preact renderer to support Preact JSX components. - renderers: ['@astrojs/renderer-preact'], - // files in `static/` will be blindly copied to `public/` - public: 'static', - // `public/` is where the built website will be output to - dist: 'public', - buildOptions: { - sitemap: true, - site: 'https://astro.build/', - }, - }); - ``` - -3. Create a file called `.gitlab-ci.yml` in the root of your project with the content below. This will build and deploy your site whenever you make changes to your content: - - ```yaml - image: node:14 - pages: - cache: - paths: - - node_modules/ - script: - - npm install - - npm run build - artifacts: - paths: - - public - only: - - main - ``` - -## Netlify - -**Note:** If you are using an older [build image](https://docs.netlify.com/configure-builds/get-started/#build-image-selection) on Netlify, make sure that you set your Node.js version in either a [`.nvmrc`](https://github.com/nvm-sh/nvm#nvmrc) file (example: `node v14.17.6`) or a `NODE_VERSION` environment variable. This step is no longer required by default. - -You can configure your deployment in two ways, via the Netlify website or with a local project `netlify.toml` file. - -### `netlify.toml` file - -Create a new `netlify.toml` file at the top level of your project repository with the following settings: - -```toml -[build] - command = "npm run build" - publish = "dist" -``` - -Push the new `netlify.toml` file up to your hosted git repository. Then, set up a new project on [Netlify](https://netlify.com) for your git repository. Netlify will read this file and automatically configure your deployment. - -### Netlify Website UI - -You can skip the `netlify.toml` file and go directly to [Netlify](https://netlify.com) to configure your project. Netlify should now detect Astro projects automatically and pre-fill the configuration for you. Make sure that the following settings are entered before hitting the "Deploy" button: - -- **Build Command:** `astro build` or `npm run build` -- **Publish directory:** `dist` - -## Google Cloud - -Different from most available deploy options here, [Google Cloud](https://cloud.google.com) requires some UI clicks to deploy projects. (Most of these actions can also be done using the gcloud CLI). - -### Cloud Run - -1. Create a new GCP project, or select one you already have. - -2. Make sure the Cloud Run API is enabled. - -3. Create a new service. - -4. Use a container from Docker Hub or build your own using [Cloud Build](https://cloud.google.com/build). - -5. Configure a port from which the files are served. - -6. Enable public access by adding a new permission to `allUsers` called `Cloud Run Invoker`. - -### Cloud Storage - -1. Create a new GCP project, or select one you already have. - -2. Create a new bucket under [Cloud Storage](https://cloud.google.com/storage). - -3. Give it a name and other required settings. - -4. Upload your `dist` folder into it or upload using [Cloud Build](https://cloud.google.com/build). - -5. Enable public access by adding a new permission to `allUsers` called `Storage Object Viewer`. - -6. Edit the website configuration and add `ìndex.html` as entrypoint and `404.html` as errorpage. - -## Google Firebase - -1. Make sure you have [firebase-tools](https://www.npmjs.com/package/firebase-tools) installed. - -2. Create `firebase.json` and `.firebaserc` at the root of your project with the following content: - - `firebase.json`: - - ```json - { - "hosting": { - "public": "dist", - "ignore": [] - } - } - ``` - - `.firebaserc`: - - ```json - { - "projects": { - "default": "<YOUR_FIREBASE_ID>" - } - } - ``` - -3. After running `npm run build`, deploy using the command `firebase deploy`. - -## Surge - -1. First install [surge](https://www.npmjs.com/package/surge), if you haven't already. - -2. Run `npm run build`. - -3. Deploy to surge by typing `surge dist`. - -You can also deploy to a [custom domain](http://surge.sh/help/adding-a-custom-domain) by adding `surge dist yourdomain.com`. - -## Heroku - -1. Install [Heroku CLI](https://devcenter.heroku.com/articles/heroku-cli). - -2. Create a Heroku account by [signing up](https://signup.heroku.com). - -3. Run `heroku login` and fill in your Heroku credentials: - - ```bash - $ heroku login - ``` - -4. Create a file called `static.json` in the root of your project with the below content: - - `static.json`: - - ```json - { - "root": "./dist" - } - ``` - - This is the configuration of your site; read more at [heroku-buildpack-static](https://github.com/heroku/heroku-buildpack-static). - -5. Set up your Heroku git remote: - - ```bash - # version change - $ git init - $ git add . - $ git commit -m "My site ready for deployment." - - # creates a new app with a specified name - $ heroku apps:create example - - # set buildpack for static sites - $ heroku buildpacks:set https://github.com/heroku/heroku-buildpack-static.git - ``` - -6. Deploy your site: - - ```bash - # publish site - $ git push heroku master - - # opens a browser to view the Dashboard version of Heroku CI - $ heroku open - ``` - -## Vercel - -You can deploy Astro to [Vercel](http://vercel.com) through the CLI or the Vercel git integrations. - -### CLI - -1. Install the [Vercel CLI](https://vercel.com/cli) and run `vercel` to deploy. -2. When asked `Want to override the settings? [y/N]`, choose `Y`. -3. Update `Output Directory` to `./dist`. -4. Your application is deployed! (e.g. [astro.vercel.app](https://astro.vercel.app/)) - -```bash -$ npm i -g vercel -$ vercel -``` - -### Git - -1. Push your code to your git repository (GitHub, GitLab, BitBucket). -2. [Import your project](https://vercel.com/new) into Vercel. -3. Update `Output Directory` to `./dist`. -4. Your application is deployed! (e.g. [astro.vercel.app](https://astro.vercel.app/)) - -After your project has been imported and deployed, all subsequent pushes to branches will generate [Preview Deployments](https://vercel.com/docs/concepts/deployments/environments#preview), and all changes made to the Production Branch (commonly “main”) will result in a [Production Deployment](https://vercel.com/docs/concepts/deployments/environments#production). - -Learn more about Vercel’s [Git Integration](https://vercel.com/docs/concepts/git). - -## Azure Static Web Apps - -You can deploy your Astro project with Microsoft Azure [Static Web Apps](https://aka.ms/staticwebapps) service. You need: - -- An Azure account and a subscription key. You can create a [free Azure account here](https://azure.microsoft.com/free). -- Your app code pushed to [GitHub](https://github.com). -- The [SWA Extension](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-azurestaticwebapps) in [Visual Studio Code](https://code.visualstudio.com). - -Install the extension in VS Code and navigate to your app root. Open the Static Web Apps extension, sign in to Azure, and click the '+' sign to create a new Static Web App. You will be prompted to designate which subscription key to use. - -Follow the wizard started by the extension to give your app a name, choose a framework preset, and designate the app root (usually `/`) and built file location `/dist`. The wizard will run and will create a GitHub action in your repo in a `.github` folder. - -The action will work to deploy your app (watch its progress in your repo's Actions tab) and, when successfully completed, you can view your app in the address provided in the extension's progress window by clicking the 'Browse Website' button that appears when the GitHub action has run. - -## Cloudflare Pages - -You can deploy your Astro project on [Cloudflare Pages](https://pages.cloudflare.com). You need: - -- A Cloudflare account. If you don’t already have one, you can create a free Cloudflare account during the process. -- Your app code pushed to a [GitHub](https://github.com) or a [GitLab](https://about.gitlab.com/) repository. - -Then, set up a new project on Cloudflare Pages. - -Use the following build settings: - -- **Framework preset**: `Astro` -- **Build command:** `npm run build` -- **Build output directory:** `dist` -- **Environment variables (advanced)**: Currently, Cloudflare Pages supports `NODE_VERSION = 12.18.0` in the Pages build environment by default. Astro requires `14.15.0`, `v16.0.0`, or higher. You can add an environment variable with the **Variable name** of `NODE_VERSION` and a **Value** of a [Node version that’s compatible with Astro](https://docs.astro.build/installation#prerequisites) or by specifying the node version of your project in a `.nvmrc` or `.node-version` file. - -Then click the **Save and Deploy** button. - -## Render - -You can deploy your Astro project on [Render](https://render.com/) following these steps: - -1. Create a [render.com account](https://dashboard.render.com/) and sign in -2. Click the **New +** button from your dashboard and select **Static Site** -3. Connect your [GitHub](https://github.com/) or [GitLab](https://about.gitlab.com/) repository or alternatively enter the public URL of a public repository -4. Give your website a name, select the branch and specify the build command and publish directory - - **build command:** `npm run build` - - **publish directory:** `dist` -5. Click the **Create Static Site** button - -## Buddy - -You can deploy your Astro project using [Buddy](https://buddy.works). To do so you'll need to: - -1. Create a **Buddy** account [here](https://buddy.works/sign-up). -2. Create a new project and connect it with a git repository (GitHub, GitLab, BitBucket, any private Git Repository or you can use Buddy Git Hosting). -3. Add a new pipeline. -4. In the newly created pipeline add a **[Node.js](https://buddy.works/actions/node-js)** action. -5. In this action add: - - ```bash - npm install - npm run build - ``` - -6. Add a deployment action - there are many to choose from, you can browse them [here](https://buddy.works/actions). Although their can settings differ, remember to set the **Source path** to `dist`. -7. Press the **Run** button. - -## Layer0 - -You can deploy your Astro project using the steps in the following sections. - -### Create the Astro Site - -If you don't have an existing Astro site, you can create one by running: - -```bash -# Make a new project directory, and navigate directly into it -$ mkdir my-astro-project && cd $_ - -# prepare for liftoff... -$ npm init astro - -# install dependencies -$ npm install - -# start developing! -$ npm run dev - -# when you're ready: build your static site to `dist/` -$ npm run build -``` - -### Add Layer0 - -```bash -# First, globally install the Layer0 CLI: -$ npm i -g @layer0/cli - -# Then, add Layer0 to your Astro site: -$ 0 init -``` - -### Update your Layer0 Router - -Paste the following into routes.ts: - -```js -// routes.ts -import { Router } from '@layer0/core'; - -export default new Router() - .get( - '/:path*/:file.:ext(js|css|png|ico|jpg|gif|svg)', - ({ cache, serveStatic }) => { - cache({ - browser: { - // cache js, css, and images in the browser for one hour... - maxAgeSeconds: 60 * 60, - }, - edge: { - // ... and at the edge for one year - maxAgeSeconds: 60 * 60 * 24 * 365, - }, - }); - serveStatic('dist/:path*/:file.:ext'); - } - ) - .match('/:path*', ({ cache, serveStatic, setResponseHeader }) => { - cache({ - // prevent the browser from caching html... - browser: false, - edge: { - // ...cache html at the edge for one year - maxAgeSeconds: 60 * 60 * 24 * 365, - }, - }); - setResponseHeader('content-type', 'text/html; charset=UTF-8'); - serveStatic('dist/:path*'); - }); -``` - -You can remove the origin backend from `layer0.config.js`: - -```js -module.exports = {}; -``` - -### Deploy to Layer0 - -To deploy your site to Layer0, run: - -```bash -# Create a production build of your astro site -$ npm run build - -# Deploy it to Layer0 -$ 0 deploy -``` - -## Credits - -This guide was originally based off [Vite](https://vitejs.dev/)’s well-documented static deploy guide. diff --git a/docs/src/pages/en/guides/environment-variables.md b/docs/src/pages/en/guides/environment-variables.md deleted file mode 100644 index c8546c8d7..000000000 --- a/docs/src/pages/en/guides/environment-variables.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -layout: ~/layouts/MainLayout.astro -title: Using environment variables -description: Learn how to use environment variables in an Astro project. ---- - -Astro uses Vite for environment variables, and allows you to use any of its methods to get and set environment variables. Note that all environment variables must be prefixed with `PUBLIC_` to be accessible by client side code. - -The ability to access private variables on the server side is [still being discussed](https://github.com/withastro/astro/issues/1765). - -## Setting environment variables - -Vite includes `dotenv` by default, allowing you to easily set environment variables without any extra configuration in Astro projects. You can also attach a mode (either `production` or `development`) to the filename, like `.env.production` or `.env.development`, which makes the environment variables only take effect in that mode. - -Just create a `.env` file in the project directory and add some variables to it. - -```bash -# .env -PUBLIC_POKEAPI="https://pokeapi.co/api/v2" -``` - -## Getting environment variables - -Instead of using `process.env`, with Vite you use `import.meta.env`, which uses the `import.meta` feature added in ES2020 (don't worry about browser support though, Vite replaces all `import.meta.env` mentions with static values). For example, to get the `PUBLIC_POKEAPI` environment variable, you could use `import.meta.env.PUBLIC_POKEAPI`. - -```js -fetch(`${import.meta.env.PUBLIC_POKEAPI}/pokemon/squirtle`); -``` - -> ⚠️WARNING⚠️: -> Because Vite statically replaces `import.meta.env`, you cannot access it with dynamic keys like `import.meta.env[key]`. - -## IntelliSense for TypeScript - -By default, Vite provides type definition for `import.meta.env` in `vite/client.d.ts`. While you can define more custom env variables in `.env.[mode]` files, you may want to get TypeScript IntelliSense for user-defined env variables which prefixed with `PUBLIC_`. - -To achieve, you can create an `env.d.ts` in `src` directory, then augment `ImportMetaEnv` like this: - -```ts -interface ImportMetaEnv { - readonly PUBLIC_POKEAPI: string; - // more env variables... -} - -interface ImportMeta { - readonly env: ImportMetaEnv; -} -``` diff --git a/docs/src/pages/en/guides/imports.md b/docs/src/pages/en/guides/imports.md deleted file mode 100644 index fffcfe925..000000000 --- a/docs/src/pages/en/guides/imports.md +++ /dev/null @@ -1,142 +0,0 @@ ---- -layout: ~/layouts/MainLayout.astro -title: Supported Imports -description: Learn how to import different content types with Astro. ---- - -Astro uses Vite as its internal build system. Vite provides Astro with built-in support for the following file types, with no configuration required: - -- JavaScript (`.js`, `.mjs`) -- TypeScript (`.ts`, `.tsx`) -- JSON (`.json`) -- JSX (`.jsx`, `.tsx`) -- CSS (`.css`) -- CSS Modules (`.module.css`) -- Images & Assets (`.svg`, `.jpg`, `.png`, etc.) -- Astro Components (`.astro`) -- Markdown (`.md`) -- WASM (`.wasm`) - -Any files in your `public/` directory are copied into the final build, untouched by Vite or Astro. The following applies to files in your `src/` directory, which Astro is ultimately responsible for. - -## JavaScript & ESM - -Astro was designed for JavaScript's native ES Module (ESM) syntax. ESM lets you define explicit imports & exports that browsers and build tools can better understand and optimize for. If you're familiar with the `import` and `export` keywords in JavaScript, then you already know ESM! - -```js -// ESM Example - src/user.js -export function getUser() { - /* ... */ -} - -// src/index.js -import { getUser } from './user.js'; -``` - -All browsers now support ESM, so Astro is able to ship this code directly to the browser during development. - -## TypeScript - -Astro includes built-in support to build TypeScript files (`*.ts`) to JavaScript. Astro components also support TypeScript in the frontmatter script section. - -Note that this built-in support is build only. By default, Astro does not type-check your TypeScript code. - -<!-- To integrate type checking into your development/build workflow, add the [@snowpack/plugin-typescript](https://www.npmjs.com/package/@snowpack/plugin-typescript) plugin. --> - -## JSX - -Astro includes built-in support to build JSX files (`*.jsx` & `*.tsx`) to JavaScript. - -If you are using Preact, Astro will detect your Preact import and switch to use the Preact-style JSX `h()` function. This is all done automatically for you. - -**Note: Astro does not support JSX in `.js`/`.ts` files.** - -## JSON - -```js -// Load the JSON object via the default export -import json from './data.json'; -``` - -Astro supports importing JSON files directly into your application. Imported files return the full JSON object in the default import. - -## CSS - -```js -// Load and inject 'style.css' onto the page -import './style.css'; -``` - -Astro supports importing CSS files directly into your application. Imported styles expose no exports, but importing one will automatically add those styles to the page. This works for all CSS files by default, and can support compile-to-CSS languages like Sass & Less via plugins. - -If you prefer not to write CSS, Astro also supports all popular CSS-in-JS libraries (ex: styled-components) for styling. - -## CSS Modules - -```jsx -// 1. Converts './style.module.css' classnames to unique, scoped values. -// 2. Returns an object mapping the original classnames to their final, scoped value. -import styles from './style.module.css'; - -// This example uses JSX, but you can use CSS Modules with any framework. -return <div className={styles.error}>Your Error Message</div>; -``` - -Astro supports CSS Modules using the `[name].module.css` naming convention. Like any CSS file, importing one will automatically apply that CSS to the page. However, CSS Modules export a special default `styles` object that maps your original classnames to unique identifiers. - -CSS Modules help you enforce component scoping & isolation on the frontend with unique-generated class names for your stylesheets. - -## Other Assets - -```jsx -import imgReference from './image.png'; // img === '/src/image.png' -import svgReference from './image.svg'; // svg === '/src/image.svg' -import txtReference from './words.txt'; // txt === '/src/words.txt' - -// This example uses JSX, but you can use import references with any framework. -<img src={imgReference} />; -``` - -All other assets not explicitly mentioned above can be imported via ESM `import` and will return a URL reference to the final built asset. This can be useful for referencing non-JS assets by URL, like creating an image element with a `src` attribute pointing to that image. - -It can also be useful to place images in the `public/`-folder as explained on the [project-structure page](/en/core-concepts/project-structure/#public). - -## WASM - -```js -// Loads and intializes the requested WASM file -const wasm = await WebAssembly.instantiateStreaming(fetch('/example.wasm')); -``` - -Astro supports loading WASM files directly into your application using the browser's [`WebAssembly`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly) API. - -## NPM Packages - -```js -// Returns the React & React-DOM npm packages -import React from 'react'; -import ReactDOM from 'react-dom'; -``` - -Astro lets you import npm packages directly in the browser. Even if a package was published using a legacy format, Astro will up-convert the package to ESM before serving it to the browser. - -When you start up your dev server or run a new build, you may see a message that Vite is "installing dependencies". This means that Vite is converting your dependencies to run in the browser. This needs to run only once, or until you next change your dependency tree by adding or removing dependencies. - -## Node Builtins - -We encourage Astro users to avoid Node.js builtins (`fs`, `path`, etc) whenever possible. Astro aims to be compatible with multiple JavaScript runtimes in the future. This includes [Deno](https://deno.land/) and [Cloudflare Workers](https://workers.cloudflare.com/) which do not support Node builtin modules such as `fs`. - -Our aim is to provide Astro alternatives to common Node.js builtins. However, no such alternatives exist today. So, if you _really_ need to use these builtin modules we don't want to stop you. Astro supports Node.js builtins using Node's newer `node:` prefix. If you want to read a file, for example, you can do so like this: - -```astro ---- -// Example: import the "fs/promises" builtin from Node.js -import fs from 'node:fs/promises'; - -const url = new URL('../../package.json', import.meta.url); -const json = await fs.readFile(url, 'utf-8'); -const data = JSON.parse(json); ---- - -<span>Version: {data.version}</span> -``` diff --git a/docs/src/pages/en/guides/markdown-content.md b/docs/src/pages/en/guides/markdown-content.md deleted file mode 100644 index 487856171..000000000 --- a/docs/src/pages/en/guides/markdown-content.md +++ /dev/null @@ -1,332 +0,0 @@ ---- -layout: ~/layouts/MainLayout.astro -title: Markdown -description: An intro to Markdown with Astro. ---- - -Astro comes with out-of-the-box Markdown support powered by the expansive [remark](https://remark.js.org/) ecosystem. - -## Parsers - -Astro lets you use any Markdown parser you want. It just needs to be a function that follows the `MarkdownParser` type declared inside [this file](https://github.com/withastro/astro/blob/main/packages/astro/src/@types/astro.ts). You can declare it inside `astro.config.mjs`: - -```js -// astro.config.mjs -export default { - markdownOptions: { - render: [ - 'parser-name', // or import('parser-name') or (contents) => {...} - { - // options - }, - ], - }, -}; -``` - -Astro comes with the `@astrojs/markdown-remark` package - the default parser. - -### Remark and Rehype Plugins - -In addition to custom components inside the [`<Markdown>` component](/en/guides/markdown-content#astros-markdown-component), the default parser comes with these plugins pre-enabled: - -- [GitHub-flavored Markdown](https://github.com/remarkjs/remark-gfm) -- [remark-smartypants](https://github.com/silvenon/remark-smartypants) -- [rehype-slug](https://github.com/rehypejs/rehype-slug) - -Also, Astro supports third-party plugins for Markdown. You can provide your plugins in `astro.config.mjs`. - -> **Note:** Enabling custom `remarkPlugins` or `rehypePlugins` removes Astro's built-in support for the plugins previously mentioned. You must explicitly add these plugins to your `astro.config.mjs` file, if desired. - -### Add a Markdown plugin in Astro - -If you want to add a plugin, you need to install the npm package dependency in your project and then update `remarkPlugins` or `rehypePlugins` inside the `@astrojs/markdown-remark` options depending on what plugin you want to have: - -```js -// astro.config.mjs -export default { - markdownOptions: { - render: [ - '@astrojs/markdown-remark', - { - remarkPlugins: [ - // Add a Remark plugin that you want to enable for your project. - // If you need to provide options for the plugin, you can use an array and put the options as the second item. - // ['remark-autolink-headings', { behavior: 'prepend'}], - ], - rehypePlugins: [ - // Add a Rehype plugin that you want to enable for your project. - // If you need to provide options for the plugin, you can use an array and put the options as the second item. - // 'rehype-slug', - // ['rehype-autolink-headings', { behavior: 'prepend'}], - ], - }, - ], - }, -}; -``` - -You can provide names of the plugins as well as import them: - -```js -import autolinkHeadings from 'remark-autolink-headings'; - -// astro.config.mjs -export default { - markdownOptions: { - render: [ - '@astrojs/markdown-remark', - { - remarkPlugins: [[autolinkHeadings, { behavior: 'prepend' }]], - }, - ], - }, -}; -``` - -### Syntax Highlighting - -Astro comes with built-in support for [Prism](https://prismjs.com/) and [Shiki](https://shiki.matsu.io/). By default, Prism is enabled. You can modify this behavior by updating the `@astrojs/markdown-remark` options: - -```js -// astro.config.mjs -export default { - markdownOptions: { - render: [ - '@astrojs/markdown-remark', - { - // Pick a syntax highlighter. Can be 'prism' (default), 'shiki' or false to disable any highlighting. - syntaxHighlight: 'prism', - // If you are using shiki, here you can define a global theme and - // add custom languages. - shikiConfig: { - theme: 'github-dark', - langs: [], - wrap: false, - }, - }, - ], - }, -}; -``` - -You can read more about custom Shiki [themes](https://github.com/shikijs/shiki/blob/main/docs/themes.md#loading-theme) and [languages](https://github.com/shikijs/shiki/blob/main/docs/languages.md#supporting-your-own-languages-with-shiki). - -## Markdown Pages - -Astro treats any `.md` files inside of the `/src/pages` directory as pages. These files can contain frontmatter, but are otherwise processed as plain markdown files and do not support components. If you're looking to embed rich components in your markdown, take a look at the [Markdown Component](#astros-markdown-component) section. - -### Layouts - -Markdown pages have a special frontmatter property for `layout`. This defines the relative path to an `.astro` component which should wrap your Markdown content, for example a [Layout](/en/core-concepts/layouts) component. All other frontmatter properties defined in your `.md` page will be exposed to the component as properties of the `content` prop. The rendered Markdown content is placed into the default `<slot />` element. - -```markdown ---- -# src/pages/index.md -layout: ../layouts/BaseLayout.astro -title: My cool page -draft: false ---- - -# Hello World! -``` - -```astro ---- -// src/layouts/BaseLayout.astro -const { content } = Astro.props; ---- -<html> - <head> - <title>{content.title}</title> - </head> - - <body> - <slot /> - </body> -</html> -``` - -For Markdown files, the `content` prop also has an `astro` property which holds special metadata about the page such as the complete Markdown `source` and a `headers` object. An example of what a blog post `content` object might look like is as follows: - -```json -{ - /** Frontmatter from a blog post - "title": "Astro 0.18 Release", - "date": "Tuesday, July 27 2021", - "author": "Matthew Phillips", - "description": "Astro 0.18 is our biggest release since Astro launch.", - "draft": false, - **/ - "astro": { - "headers": [ - { - "depth": 1, - "text": "Astro 0.18 Release", - "slug": "astro-018-release" - }, - { - "depth": 2, - "text": "Responsive partial hydration", - "slug": "responsive-partial-hydration" - } - /* ... */ - ], - "source": "# Astro 0.18 Release\\nA little over a month ago, the first public beta [...]" - }, - "url": "" -} -``` - -> Keep in mind that the only guaranteed properties coming from the `content` prop are `astro` and `url`. - -### Images and videos - -Using images or videos follows Astro's normal import rules: - -- Place them in the `public/` as explained on the [project-structure page](/en/core-concepts/project-structure/#public) - - Example: Image is located at `/public/assets/img/astonaut.png` → Markdown: `` -- Or use `import` as explained on the [imports page](/en/guides/imports#other-assets) (when using Astro's Markdown Component) - -### Markdown draft pages - -Markdown pages which have the property `draft` set in their frontmatter are referred to as "draft pages". By default, Astro excludes these pages from the build when building the static version of your page (i.e `astro build`), which means that you can exclude draft/incomplete pages from the production build by setting `draft` to `true`. To enable building of draft pages, you can set `buildOptions.drafts` to `true` in the configuration file, or pass the `--drafts` flag when running `astro build`. Markdown pages which do not have the `draft` property set are not affected. An example of a markdown draft page can be: - -```markdown ---- -# src/pages/blog-post.md -title: My Blog Post -draft: true ---- - -This is my blog post which is currently incomplete. -``` - -An example of a markdown post which is not a draft: - -```markdown ---- -# src/pages/blog-post.md -title: My Blog Post -draft: false ---- - -This is my blog post... -``` - -> This feature only applies to local markdown pages, not the `<Markdown />` component, or remote markdown. - -## Astro's Markdown Component - -Astro has a dedicated component used to let you render your markdown as HTML components. This is a special component that is only exposed to `.astro` files. To use the `<Markdown>` component, within your frontmatter block use the following import statement: - -```astro ---- -import { Markdown } from 'astro/components'; ---- -``` - -You can utilize this within your `.astro` file by doing the following: - -```astro ---- -import { Markdown } from 'astro/components'; ---- - -<Layout> - <Markdown> - # Hello world! - - The contents inside here is all in markdown. - </Markdown> -</Layout> -``` - -`<Markdown>` components provide more flexibility and allow you to use plain HTML or custom components. For example: - -````astro ---- -// For now, this import _must_ be named "Markdown" and _must not_ be wrapped with a custom component -// We're working on easing these restrictions! -import { Markdown } from 'astro/components'; -import Layout from '../layouts/main.astro'; -import MyFancyCodePreview from '../components/MyFancyCodePreview.tsx'; - -const expressions = 'Lorem ipsum'; ---- - -<Layout> - <Markdown> - # Hello world! - - **Everything** supported in a `.md` file is also supported here! - - There is _zero_ runtime overhead. - - In addition, Astro supports: - - Astro {expressions} - - Automatic indentation normalization - - Automatic escaping of expressions inside code blocks - - ```js - // This content is not transformed! - const object = { someOtherValue }; - ``` - - - Rich component support like any `.astro` file! - - Recursive Markdown support (Component children are also processed as Markdown) - - <MyFancyCodePreview client:visible> - ```js - const object = { someOtherValue }; - ``` - </MyFancyCodePreview client:visible> - </Markdown> -</Layout> -```` - -## Remote Markdown - -If you have Markdown in a remote source, you may pass it directly to the Markdown component through the `content` attribute. For example, the example below fetches the README from Snowpack's GitHub repository and renders it as HTML. - -```astro ---- -import { Markdown } from 'astro/components'; - -const content = await fetch('https://raw.githubusercontent.com/snowpackjs/snowpack/main/README.md').then(res => res.text()); ---- - -<Layout> - <Markdown content={content} /> -</Layout> -``` - -There might be times when you want to combine both dynamic, and static markdown. If that is the case, you can nest `<Markdown>` components with each other to get the best of both worlds. - -```astro ---- -import { Markdown } from 'astro/components'; - -const content = await fetch('https://raw.githubusercontent.com/snowpackjs/snowpack/main/README.md').then(res => res.text()); ---- - -<Layout> - <Markdown> - ## Markdown example - - Here we have some __Markdown__ code. We can also dynamically render content from remote places. - - <Markdown content={content} /> - </Markdown> -</Layout> -``` - -## Security FAQs - -**Aren't there security concerns to rendering remote markdown directly to HTML?** - -Yes! Just like with regular HTML, improper use of the `Markdown` component can open you up to a [cross-site scripting (XSS)](https://en.wikipedia.org/wiki/Cross-site_scripting) attack. If you are rendering untrusted content, be sure to _sanitize your content **before** rendering it_. - -**Why not use a prop like React's `dangerouslySetInnerHTML={{ __html: content }}`?** - -Rendering a string of HTML (or Markdown) is an extremely common use case when rendering a static site and you probably don't need the extra hoops to jump through. Rendering untrusted content is always dangerous! Be sure to _sanitize your content **before** rendering it_. diff --git a/docs/src/pages/en/guides/pagination.md b/docs/src/pages/en/guides/pagination.md deleted file mode 100644 index 73fc2ef96..000000000 --- a/docs/src/pages/en/guides/pagination.md +++ /dev/null @@ -1,108 +0,0 @@ ---- -layout: ~/layouts/MainLayout.astro -title: Pagination -description: An intro to Astro pagination to split large amounts of data into different pages. ---- - -Astro supports built-in, automatic pagination for large collections of data that need to be split into multiple pages. Astro also automatically includes pagination metadata for things like previous/next page URL, total number of pages, and more. - -## When to use pagination - -Pagination is only useful when you need to generate multiple, numbered pages from a larger data set. - -If all of your data can fit on a single page then you should consider using a static [page component](/en/core-concepts/astro-pages) instead. - -If you need to split your data into multiple pages but do not want those page URLs to be numbered, then you should use a [dynamic page](/en/core-concepts/routing) instead without pagination (Example: `/tag/[tag].astro`). - -## How to use pagination - -### Create your page component - -To automatically paginate some data, you'll first need to create your page component. This is the component `.astro` file that every page in the paginated collection will inherit from. - -Pagination is built on top of dynamic page routing, with the page number in the URL represented as a dynamic route param: `[page].astro` or `[...page].astro`. If you aren't familiar with routing in Astro, quickly familiarize yourself with our [Routing documentation](/en/core-concepts/routing) before continuing. - -Your first page URL will be different depending on which type of query param you use: - -- `/posts/[page].astro` will generate the URLs `/posts/1`, `/posts/2`, `/posts/3`, etc. -- `/posts/[...page].astro` will generate the URLs `/posts`, `/posts/2`, `/posts/3`, etc. - -### calling the `paginate()` function - -Once you have decided on the file name/path for your page component, you'll need to export a [`getStaticPaths()`](/en/reference/api-reference#getstaticpaths) function from the component. `getStaticPaths()` is where you tell Astro what pages to generate. - -`getStaticPaths()` provides the `paginate()` function that we'll use to paginate your data. In the example below, we'll use `paginate()` to split a list of 150 Pokemon into 15 pages of 10 Pokemon each. - -```js -export async function getStaticPaths({ paginate }) { - // Load your data with fetch(), Astro.fetchContent(), etc. - const response = await fetch(`https://pokeapi.co/api/v2/pokemon?limit=150`); - const result = await response.json(); - const allPokemon = result.results; - // Return a paginated collection of paths for all posts - return paginate(allPokemon, { pageSize: 10 }); -} -// If set up correctly, The page prop now has everything that -// you need to render a single page (see next section). -const { page } = Astro.props; -``` - -`paginate()` generates the correct array of path objects for `getStaticPaths()`. This automatically tells Astro to create a new URL for every page of the collection. The page data will then be passed as a `page` prop to the `.astro` page component. - -### using the `page` prop - -Once you've set up your page component and defined your `getStaticPaths()` function, you're ready to design your page template. Each page in the paginated collection will be passed its data in the `page` prop. - -```astro ---- -export async function getStaticPaths { /* ... */ } -const { page } = Astro.props; ---- -<h1>Page {page.currentPage}</h1> -<ul> - {page.data.map(item => <li>{item.title}</li>)} -</ul> -``` - -The `page` prop has several useful properties, but the most important one is `page.data`. This is the array containing the page's slice of data that you passed to the `paginate()` function. For example, if you called `paginate()` on an array of 150 Pokemon: - -- `/1`: `page.data` would be an array of the first 10 Pokemon -- `/2`: `page.data` would be an array of Pokemon 11-20 -- `/3`: `page.data` would be an array of Pokemon 21-30 -- etc. etc. - -The `page` prop includes other helpful metadata, like `page.url.next`, `page.url.prev`, `page.total`, and more. See our [API reference](/en/reference/api-reference#the-pagination-page-prop) for the full `page` interface. - -## Nested pagination - -A more advanced use-case for pagination is **nested pagination.** This is when pagination is combined with other dynamic route params. You can use nested pagination to group your paginated collection by some property or tag. - -For example, if you want to group your paginated markdown posts by some tag, you would use nested pagination by creating a `/src/pages/[tag]/[page].astro` page that would match the following URLS: - -- `/red/1` (tag=red) -- `/red/2` (tag=red) -- `/blue/1` (tag=blue) -- `/green/1` (tag=green) - -Nested pagination works by returning an array of `paginate()` results from `getStaticPaths()`, one for each grouping. In the following example, we will implement nested pagination to build the URLs listed above: - -```astro ---- -// Example: /src/pages/[tag]/[page].astro -export function getStaticPaths({paginate}) { - const allTags = ['red', 'blue', 'green']; - const allPosts = Astro.fetchContent('../../posts/*.md'); - // For every tag, return a paginate() result. - // Make sure that you pass `{params: {tag}}` to `paginate()` - // so that Astro knows which tag grouping the result is for. - return allTags.map((tag) => { - const filteredPosts = allPosts.filter((post) => post.tag === tag); - return paginate(filteredPosts, { - params: { tag }, - pageSize: 10 - }); - }); -} -const { page } = Astro.props; -const { params } = Astro.request; -``` diff --git a/docs/src/pages/en/guides/publish-to-npm.md b/docs/src/pages/en/guides/publish-to-npm.md deleted file mode 100644 index 2b3d88932..000000000 --- a/docs/src/pages/en/guides/publish-to-npm.md +++ /dev/null @@ -1,216 +0,0 @@ ---- -layout: ~/layouts/MainLayout.astro -title: Publish to NPM -description: Learn how to publish Astro components to NPM ---- - -Building a new Astro component? **Publish it to [npm!](https://npmjs.com/)** - -Publishing a component is a great way to reuse work across your team, your company, or the entire world. Astro components can be published to and installed from npm, just like any other JavaScript package. - -**Astro's ability to publish and reuse popular components is one of it's most powerful features!** - -Even if you don't plan on publishing your components online, the patterns outlined below can help any developer design reusable components in isolation from their custom website or business logic. - -Looking for inspiration? Check out some of [our favorite themes & components][/themes] from the Astro community. You can also [search npm](https://www.npmjs.com/search?q=keywords:astro-component) to see the entire public catalog. - -## Creating a package - -> Before diving in, it will help have a basic understanding of: -> -> - [Node Modules](https://docs.npmjs.com/creating-node-js-modules) -> - [JSON Manifest (`package.json`)](https://docs.npmjs.com/creating-a-package-json-file) -> - [Workspaces](https://docs.npmjs.com/cli/v7/configuring-npm/package-json#workspaces) - -To create a new package, we recommend developing with **workspaces**. This will allow you to develop your component alongside a working copy of Astro. - -``` -my-project/ - ├─ demo/ - └─ ... for testing and demonstration - ├─ package.json - └─ packages/ - └─ my-component/ - ├─ index.js - ├─ package.json - └─ ... additional files used by the package -``` - -In this example, named `my-project`, we create a project with a single package, named `my-component`, and a `demo` directory for testing and demonstrating the component. - -This is configured in the project root’s `package.json` file. - -```json -{ - "name": "my-project", - "workspaces": ["demo", "packages/*"] -} -``` - -In this example, multiple packages can be developed together from the `packages` directory. These packages can also be referenced from `demo`, where you can install a working copy of Astro. - -```shell -npm init astro demo --template minimal -``` - -Now let’s explore the files that will make up your individual package: - -### `package.json` - -The `package.json` in the package directory includes all of the information related to your package, including its description, dependencies, and any other package metadata. - -```json -{ - "name": "my-component", - "description": "... description", - "version": "1.0.0", - "type": "module", - "exports": { - ".": "./index.js", - "./astro": "./MyAstroComponent.astro", - "./react": "./MyReactComponent.jsx" - }, - "files": ["index.js", "MyAstroComponent.astro", "MyReactComponent.jsx"], - "keywords": ["astro-component", "... etc", "... etc"] -} -``` - -#### `package.json#description` - -The short description of your component used to help others know what it does. - -```json -{ - "description": "An Astro Element Generator" -} -``` - -#### `package.json#type` - -The module format used by Node.js and Astro to interpret your `index.js` files. - -```json -{ - "type": "module" -} -``` - -We recommend using `"type": "module"` so that your `index.js` can be used as an entrypoint with `import` and `export`. - -#### `package.json#exports` - -The entry points allowed by Astro to import your component or any of its [files](#packagejsonfiles). - -```json -{ - "exports": { - ".": "./index.js", - "./astro": "./MyAstroComponent.astro", - "./react": "./MyReactComponent.jsx" - } -} -``` - -In this example, importing `my-component` would use `index.js`, while importing `my-component/astro` or `my-component/react` would use `MyAstroComponent.astro` or `MyReactComponent.jsx`. - -#### `package.json#files` - -```json -{ - "files": ["index.js", "MyAstroComponent.astro", "MyReactComponent.jsx"] -} -``` - -#### `package.json#keywords` - -An array of keywords relevant to your component that are used to help others [find your component on npm](https://www.npmjs.com/search?q=keywords:astro-component) and any other search catalogs. - -We recommend adding the `astro-component` as a special keyword to maximize its discoverability in the Astro ecosystem. - -```json -{ - "keywords": ["astro-component", "... etc", "... etc"] -} -``` - ---- - -### `index.js` - -The main **package entrypoint** used whenever your package is imported. - -```js -export { default as MyAstroComponent } from './MyAstroComponent.astro'; - -export { default as MyReactComponent } from './MyReactComponent.jsx'; -``` - -This allows you to package multiple components together into a single interface. - -#### Example: Using Named Imports - -```astro ---- -import { MyAstroComponent } from 'my-component'; -import { MyReactComponent } from 'my-component'; ---- -<MyAstroComponent /> -<MyReactComponent /> -``` - -#### Example: Using Namespace Imports - -```astro ---- -import * as Example from 'example-astro-component'; ---- -<Example.MyAstroComponent /> -<Example.MyReactComponent /> -``` - -#### Example: Using Individual Imports - -```astro ---- -import MyAstroComponent from 'example-astro-component/astro'; -import MyReactComponent from 'example-astro-component/react'; ---- -<MyAstroComponent /> -<MyReactComponent /> -``` - ---- - -## Developing your package - -Astro does not have a dedicated "package mode" for development. Instead, you should use a demo project to develop and test your package inside of your project. This can be a private website only used for development, or a public demo/documentation website for your package. - -If you are extracting components from an existing project, you can even continue to use that project to develop your now-extracted components. - -## Testing your component - -Astro does not currently ship a test runner. This is something that we would like to tackle before our v1.0 release. _(If you are interested in helping out, [join us on Discord!](https://astro.build/chat))_ - -In the meantime, our current recommendation for testing is: - -1. Add a test `fixtures` directory to your `demo/src/pages` directory. -2. Add a new page for every test that you'd like to run. -3. Each page should include some different component usage that you'd like to test. -4. Run `astro build` to build your fixtures, then compare the output of the `dist/__fixtures__/` directory to what you expected. - -```bash -my-project/demo/src/pages/__fixtures__/ - ├─ test-name-01.astro - ├─ test-name-02.astro - └─ test-name-03.astro -``` - -## Publishing your component - -Once you have your package ready, you can publish it to npm! - -To publish a package to npm, use the `npm publish` command. If that fails, make sure that you've logged in via `npm login` and that your package.json is correct. If it succeeds, you're done! - -Notice that there was no `build` step for Astro packages. Any file type that Astro supports can be published directly without a build step, because we know that Astro already supports them natively. This includes all files with extensions like `.astro`, `.ts`, `.jsx`, and `.css`. - -If you need some other file type that isn't natively supported by Astro, you are welcome to add a build step to your package. This advanced exercise is left up to you. diff --git a/docs/src/pages/en/guides/rss.md b/docs/src/pages/en/guides/rss.md deleted file mode 100644 index 52c80ea2d..000000000 --- a/docs/src/pages/en/guides/rss.md +++ /dev/null @@ -1,53 +0,0 @@ ---- -layout: ~/layouts/MainLayout.astro -title: RSS -description: An intro to RSS in Astro ---- - -Astro supports fast, automatic RSS feed generation for blogs and other content websites. For more information about RSS feeds in general, see [aboutfeeds.com](https://aboutfeeds.com/). - -You can create an RSS feed from any Astro page that uses a `getStaticPaths()` function for routing. Only dynamic routes can use `getStaticPaths()` today (see [Routing](/en/core-concepts/routing)). - -> We hope to make this feature available to all other pages before v1.0. As a workaround, you can convert a static route to a dynamic route that only generates a single page. See [Routing](/en/core-concepts/routing) for more information about dynamic routes. - -Create an RSS Feed by calling the `rss()` function that is passed as an argument to `getStaticPaths()`. This will create an `rss.xml` file in your final build based on the data that you provide using the `items` array. - -```js -// Example: /src/pages/posts/[...page].astro -// Place this function inside your Astro component script. -export async function getStaticPaths({rss}) { - const allPosts = Astro.fetchContent('../post/*.md'); - const sortedPosts = allPosts.sort((a, b) => new Date(b.date) - new Date(a.date)); - // Generate an RSS feed from this collection - rss({ - // The RSS Feed title, description, and custom metadata. - title: 'Don\'s Blog', - // See "Styling" section below - stylesheet: true, - description: 'An example blog on Astro', - customData: `<language>en-us</language>`, - // The list of items for your RSS feed, sorted. - items: sortedPosts.map(item => ({ - title: item.title, - description: item.description, - link: item.url, - pubDate: item.date, - })), - // Optional: Customize where the file is written to. - // Otherwise, defaults to "/rss.xml" - dest: "/my/custom/feed.xml", - }); - // Return your paths - return [...]; -} -``` - -Note: RSS feeds will **not** be built during development. Currently, RSS feeds are only generated during your final build. - -### Styling - -RSS Feeds can be styled with an XSL stylesheet for a more pleasant user experience when they are opened directly in a browser. By default, Astro does not set a stylesheet for RSS feeds, but it can be enabled by setting the `stylesheet` option. - -Astro can automatically use [Pretty Feed](https://github.com/genmon/aboutfeeds/blob/main/tools/pretty-feed-v3.xsl), a popular open-source XSL stylesheet. To enable this behavior, pass `stylesheet: true`. - -If you'd like to use a custom XSL stylesheet, you can pass a string value like `stylesheet: '/my-custom-stylesheet.xsl'`. This file should be in your `public/` directory (in this case, `public/my-custom-stylesheet.xsl`). diff --git a/docs/src/pages/en/guides/styling.md b/docs/src/pages/en/guides/styling.md deleted file mode 100644 index 5687ff7b8..000000000 --- a/docs/src/pages/en/guides/styling.md +++ /dev/null @@ -1,640 +0,0 @@ ---- -layout: ~/layouts/MainLayout.astro -title: Styling & CSS -description: Learn how to style components with Astro. ---- - -Astro includes special handling to make writing CSS as easy as possible. Styling inside of Astro components is done by adding a `<style>` tag anywhere. - -## Astro component styles - -By default, all Astro component styles are **scoped**, meaning they only apply to the current component. This can be very easy to work with, as you only have to worry about what’s in your current document at any given time. - -```html -<!-- src/components/MyComponent.astro --> -<style> - /* Scoped class selector within the component */ - .text { - font-family: cursive; - } - /* Scoped element selector within the component */ - h1 { - color: red; - } -</style> - -<h1>I’m a scoped style and I’m red!</h1> -<p class="text">I'm a scoped style and I’m cursive!</p> -``` - -Note that the `h1` selector won’t bleed out of the current component! These styles won’t apply any other `h1` tags outside this document. Not even child components. - -_Tip: even though you can use element selectors, using classnames is preferred. This is not only slightly more performant, but is also easier to read, especially in a large document._ - -### Global styles - -Of course, the real power of CSS is being able to reuse as much as possible! The preferred method of loading global styles is by using a standard `<link>` tag like you’re used to. It can even be used in conjunction with Astro’s scoped `<style>` tag: - -```html -<!-- src/pages/index.astro --> -<head> - <!-- load styles from src/styles/utils.css using Astro.resolve() --> - <link rel="stylesheet" type="text/css" - href={Astro.resolve('../styles/utils.css')} /> -</head> -<body> - <!-- scoped Astro styles that apply only to the current page (not to children or other components) --> - <style> - .title { - font-size: 32px; - font-weight: bold; - } - </style> - - <!-- the ".title" class is scoped, but we can also use our global "align-center" and "margin top: 4" utility classes from utils.css --> - <h1 class="title align-center mt4">Scoped Page Title</h1> -</body> -``` - -_Note: `Astro.resolve()` is a handy utility that helps resolve files from anywhere ([docs][astro-resolve])_ - -#### Styling children - -If you’d like scoped styles to apply to children, you can use the special `:global()` function borrowed from [CSS Modules][css-modules]: - -```astro -<!-- src/components/MyComponent.astro --> ---- -import PostContent from './Post.astro'; ---- -<style> - /* Scoped to current component only */ - h1 { - color: red; - } - - /* Scoped to all descendents of the scoped .blog-post class */ - .blog-post :global(h1) { - color: blue; - } -</style> - -<h1>Title</h1> -<article class="blog-post"> - <PostContent /> -</article> -``` - -This is a great way to style things like blog posts, or documents with CMS-powered content where the contents live outside of Astro. But be careful when styling children unconditionally, as it breaks component encapsulation. Components that appear different based on whether or not they have a certain parent component can become unwieldy quickly. - -#### Global styles within style tag - -If you’d like to use global styles but you don’t want to use a normal `<link>` tag (recommended), there is a `<style global>` escape hatch: - -```html -<style global> - /* Applies to all h1 tags in your entire site */ - h1 { - font-size: 32px; - } -</style> - -<h1>Globally-styled</h1> -``` - -You can achieve the same by using the `:global()` function at the root of a selector: - -```html -<style> - /* Applies to all h1 tags in your entire site */ - :global(h1) { - font-size: 32px; - } - - /* normal scoped h1 that applies to this file only */ - h1 { - color: blue; - } -</style> -``` - -It’s recommended to only use this in scenarios where a `<link>` tag won’t work. It’s harder to track down errant global styles when they’re scattered around and not in a central CSS file. - -📚 Read our full guide on [Astro component syntax][astro-component] to learn more about using the `<style>` tag. - -## Autoprefixer - -[Autoprefixer][autoprefixer] takes care of cross-browser CSS compatibility for you. Use it in astro by installing it (`npm install --save-dev autoprefixer`) and adding a `postcss.config.cjs` file to the root of your project: - -```js -// postcss.config.cjs -module.exports = { - plugins: [require('autoprefixer')], -}; -``` - -_Note: Astro v0.21 and later requires this manual setup for autoprefixer. Previous versions ran this automatically._ - -## PostCSS - -You can use any PostCSS plugin by adding a `postcss.config.cjs` file to the root of your project. Follow the documentation for the plugin you’re trying to install for configuration and setup. - ---- - -## Supported Styling Options - -Styling in Astro is meant to be as flexible as you'd like it to be! The following options are all supported: - -| Framework | Global CSS | Scoped CSS | CSS Modules | -| :--------------- | :--------: | :--------: | :---------: | -| `.astro` | ✅ | ✅ | N/A¹ | -| `.jsx` \| `.tsx` | ✅ | ❌ | ✅ | -| `.vue` | ✅ | ✅ | ✅ | -| `.svelte` | ✅ | ✅ | ❌ | - -¹ _`.astro` files have no runtime, therefore Scoped CSS takes the place of CSS Modules (styles are still scoped to components, but don't need dynamic values)_ - -All styles in Astro are automatically minified and bundled, so you can just write CSS and we'll handle the rest ✨. - ---- - -## Frameworks and Libraries - -### 📘 React / Preact - -`.jsx` files support both global CSS and CSS Modules. To enable the latter, use the `.module.css` extension (or `.module.scss`/`.module.sass` if using Sass). - -```js -import './global.css'; // include global CSS -import Styles from './styles.module.css'; // Use CSS Modules (must end in `.module.css`, `.module.scss`, or `.module.sass`!) -``` - -### 📗 Vue - -Vue in Astro supports the same methods as `vue-loader` does: - -- [vue-loader - Scoped CSS][vue-scoped] -- [vue-loader - CSS Modules][vue-css-modules] - -### 📕 Svelte - -Svelte in Astro also works exactly as expected: [Svelte Styling Docs][svelte-style]. - -### 🎨 CSS Preprocessors (Sass, Stylus, etc.) - -Astro supports CSS preprocessors such as [Sass][sass], [Stylus][stylus], and [Less][less] through [Vite][vite-preprocessors]. It can be enabled via the following: - -- **Sass**: Run `npm install -D sass` and use `<style lang="scss">` or `<style lang="sass">` (indented) in `.astro` files -- **Stylus**: Run `npm install -D stylus` and use `<style lang="styl">` or `<style lang="stylus">` in `.astro` files -- **Less**: Run `npm install -D less` and use `<style lang="less">` in `.astro` files. - -You can also use all of the above within JS frameworks as well! Simply follow the patterns each framework recommends: - -- **React** / **Preact**: `import Styles from './styles.module.scss'`; -- **Vue**: `<style lang="scss">` -- **Svelte**: `<style lang="scss">` - -Additionally, [PostCSS](#-postcss) is supported, but the setup is [slightly different](#-postcss). - -_Note: CSS inside `public/` will **not** be transformed! Place it within `src/` instead._ - -### 🍃 Tailwind - -Astro can be configured to use [Tailwind][tailwind] easily! Install the dependencies: - -``` -npm install --save-dev tailwindcss -``` - -And create 2 files in your project root: `tailwind.config.cjs` and `postcss.config.cjs`: - -```js -// tailwind.config.cjs -module.exports = { - content: [ - './public/**/*.html', - './src/**/*.{astro,js,jsx,svelte,ts,tsx,vue}', - ], - // more options here -}; -``` - -```js -// postcss.config.cjs -module.exports = { - plugins: [require('tailwindcss')], -}; -``` - -Now you're ready to write Tailwind! Our recommended approach is to create a `src/styles/global.css` file (or whatever you‘d like to name your global stylesheet) with [Tailwind utilities][tailwind-utilities] like so: - -```css -/* src/styles/global.css */ -@tailwind base; -@tailwind components; -@tailwind utilities; -``` - -Lastly, add it to your Astro page (or layout template): - -```astro -<head> - <style global> - @import "../styles/global.css"; - </style> -</head> -``` - -As an alternative to `src/styles/global.css`, You may also add Tailwind utilities to individual `pages/*.astro` components in `<style>` tags, but be mindful of duplication! If you end up creating multiple Tailwind-managed stylesheets for your site, make sure you're not sending the same CSS to users over and over again in separate CSS files. - -#### Migrating from v0.19 - -As of [version 0.20.0](https://github.com/withastro/astro/releases/tag/astro%400.20.0), Astro will no longer bundle, build and process `public/` files. Previously, we'd recommended putting your tailwind files in the `public/` directory. If you started a project with this pattern, you should move any Tailwind styles into the `src` directory and import them in your template using [Astro.resolve()][astro-resolve]: - -```astro - <link - rel="stylesheet" - href={Astro.resolve("../styles/global.css")} - > -``` - -### 🎭 PostCSS - -Using PostCSS is as simple as placing a [`postcss.config.cjs`](https://github.com/postcss/postcss#usage) file in the root of your project. - -Be aware that this plugin will run on all CSS in your project, including any files that compiled to CSS (like `.scss` Sass files, for example). - -_Note: CSS in `public/` **will not be transformed!** Instead, place it within `src/` if you’d like PostCSS to run over your styles._ - -## Bundling - -All CSS is minified and bundled automatically for you in running `astro build`. Without getting too in the weeds, the general rules are: - -- If a style only appears on one route, it's only loaded for that route (`/_astro/[page]-[hash].css`) -- If a style appears on multiple routes, it's deduplicated into a `/_astro/common-[hash].css` bundle -- All styles are hashed according to their contents (the hashes only change if the contents do!) - -We'll be expanding our styling optimization story over time, and would love your feedback! If `astro build` generates unexpected styles, or if you can think of improvements, [please open an issue][issues]. - -_Note: be mindful when some page styles get extracted to the "common" bundle, and some page styles stay on-page. For most people this may not pose an issue, but when part of your styles are bundled they technically may load in a different order and your cascade may be different. While this problem isn't unique to Astro and is present in almost any CSS bundling process, it can be unexpected if you're not anticipating it. Be sure to inspect your final production build, and please [report any issues][issues] you may come across._ - -## Advanced Styling Architecture - -Too many development setups take a hands-off approach to CSS, or at most leave you with only contrived examples that don't get you very far. Telling developers "Use whatever styling solution you want!" is a nice thought that rarely works out in practice. Few styling approaches lend themselves to every setup. Astro is no different—certain styling approaches _will_ work better than others. - -An example to illustrate this: Astro removes runtime JS (even the core framework if possible). Thus, depending on Styled Components for all your styles would be bad, as that would require React to load on pages where it's not needed. Or at best, you'd get a "[FOUC][fouc]" as your static HTML is served but the user waits for JavaScript to download and execute. Or consider a second example at the opposite end of the spectrum: _BEM_. You _can_ use a completely-decoupled [BEM][bem] or [SMACSS][smacss] approach in Astro. But that's a lot of manual maintenance you can avoid, and it leaves out a lot of convenience of [Astro components](/en/core-concepts/astro-components). - -We think there's a great middle ground between intuitive-but-slow CSS-in-JS and fast-but-cumbersome global CSS: **Hybrid Scoped + Utility CSS**. This approach works well in Astro, is performant for users, and will be the best styling solution in Astro _for most people_ (provided you're willing to learn a little). So as a quick recap: - -**This approach is good for…** - -- Developers wanting to try out something new in regard to styling -- Developers that would appreciate some strong opinions in CSS architecture - -**This approach is **NOT** good for…** - -- Developers that already have strong opinions on styling, and want to control everything themselves - -Read on if you're looking for some strong opinions 🙂. We'll describe the approach by enforcing a few key rules that should govern how you set your styles: - -### Hybrid Scoped + Utility CSS - -#### Scoped styles - -You don't need an explanation on component-based design. You already know that reusing components is a good idea. And it's this idea that got people used to concepts like [Styled Components][styled-components] and [Styled JSX][styled-jsx]. But rather than burden your users with slow load times of CSS-in-JS, Astro has something better: **built-in scoped styles.** - -```astro ---- -// src/components/Button.astro --> ---- -<style lang="scss"> - /* ✅ Locally scoped! */ - .btn { - padding: 0.5em 1em; - border-radius: 3px; - font-weight: 700; - } -</style> -<button type="button" class="btn"> - <slot></slot> -</button> -``` - -_Note: all the examples here use `lang="scss"` which is a great convenience for nesting, and sharing [colors and variables][sass-use], but it's entirely optional and you may use normal CSS if you wish._ - -That `.btn` class is scoped within that component, and won't leak out. It means that you can **focus on styling and not naming.** Local-first approach fits in very well with Astro's ESM-powered design, favoring encapsulation and reusability over global scope. While this is a simple example, it should be noted that **this scales incredibly well.** And if you need to share common values between components, [Sass' module system][sass-use] also gets our recommendation for being easy to use, and a great fit with component-first design. - -By contrast, Astro does allow global styles via the `:global()` and `<style global>` escape hatches. However, this should be avoided if possible. To illustrate this: say you used your button in a `<Nav />` component, and you wanted to style it differently there. You might be tempted to have something like: - -```astro ---- -// src/components/Nav.astro -import Button from './Button.astro'; ---- - -<style lang="scss"> - .nav :global(.btn) { - /* ❌ This will fight with <Button>'s styles */ - } -</style> - -<nav class="nav"> - <Button>Menu</Button> -</nav> -``` - -This is undesirable because now `<Nav>` and `<Button>` fight over what the final button looks like. Now, whenever you edit one, you'll always have to edit the other, and they are no longer truly isolated as they once were (now coupled by a bidirectional styling dependency). It's easy to see how this pattern only has to be repeated a couple times before being afraid that touching any styles _anywhere_ may break styling in a completely different part of the app (queue `peter-griffin-css-blinds.gif`). - -Instead, let `<Button>` control its own styles, and try a prop: - -```astro ---- -// src/components/Button.astro -const { theme } = Astro.props; ---- -<style lang="scss"> - .btn { - /* ✅ <Button> is now back in control of its own styling again! */ - &[data-theme='nav'] { - // nav-friendly styles here… - } - } -</style> - -<button type="button" data-theme={theme}> - <slot></slot> -</button> -``` - -Elsewhere, you can use `<Button theme="nav">` to set the type of button it is. This preserves the contract of _Button is in charge of its styles, and Nav is in charge of its styles_, and now you can edit one without affecting the other. The worst case scenario of using global styles is that the component is broken and unusable (it's missing part of its core styles). But the worst case scenario of using props (e.g. typo) is that a component will only fall back to its default, but still usable, state. - -💁 **Why this works well in Astro**: Astro is inspired most by JavaScript modules: you only need to know about what's in one file at a time, and you never have to worry about something in a remote file affecting how this code runs. But we're not alone in this; Vue and Svelte have both capitalized on and popularized the idea that styles and markup are natural fits in the same component file. [You can still have separation of concerns][peace-on-css] even with markup, styling, and logic contained in one file. In fact, that's what makes component design so powerful! So write CSS without fear that you picked a name that's used by some other component across your app. - -#### Utility CSS - -Recently there has been a debate of all-scoped component styles vs utility-only CSS. But we agree with people like Sarah Dayan who ask [why can't we have both][utility-css]? Truth is that while having scoped component styles are great, there are still hundreds of times when the website's coming together when two components just don't line up _quite_ right, and one needs a nudge. Or different text treatment is needed in one component instance. - -While the thought of having perfect, pristine components is nice, it's unrealistic. No design system is absolutely perfect, and every design system has inconsistencies. And it's in reconciling these inconsistencies where components can become a mess without utility CSS. Utility CSS is great for adding minor tweaks necessary to get the website out the door. But they also are incomplete on their own—if you've ever tried to manage responsive styles or accessible focus states with utility CSS it can quickly become a mess! **Utility CSS works best in partnership with component (scoped) CSS**. And in order to be as easy as possible to use, Utility CSS should be global (arguably should be your only global CSS, besides maybe reset.css) so you don't have to deal with imports all willy-nilly. - -Some great problems best handled with Utility CSS are: - -- [margin](https://github.com/drwpow/sass-utils#-margin--padding) -- [padding](https://github.com/drwpow/sass-utils#-margin--padding) -- [text/background color](https://github.com/drwpow/sass-utils#-color) -- [font size and family](https://github.com/drwpow/sass-utils#%F0%9F%85%B0%EF%B8%8F-font--text) -- [default element styling](https://github.com/kognise/water.css) - -In Astro, we recommend the following setup for this: - -```html -<head> - <link rel="stylesheet" href="/styles/global.css" /> -</head> -``` - -And in your local filesystem, you can even use Sass' [@use][sass-use] to combine files together effortlessly: - -``` -├── src/ -│ └── styles/ -│ ├── _base.scss -│ ├── _tokens.scss -│ ├── _typography.scss -│ ├── _utils.scss -│ └── global.scss -``` - -What's in each file is up to you to determine, but start small, add utilities as you need them, and you'll keep your CSS weight incredibly low. And utilities you wrote to meet your real needs will always be better than anything off the shelf. - -So to recap, think of scoped styles as the backbone of your styles that get you 80% of the way there, and utility CSS filling in the remaining 20%. They both work well in tandem, with each compensating for the other's weakness. - -💁 **Why this works well in Astro**: Astro was built around the idea of **Scoped CSS and Global Utility CSS living together in harmony** ♥️! Take full advantage of it. - -### More suggestions - -"But wait!" you may ask, having read the previous section. "That doesn't take care of [my usecase]!" If you‘re looking for more pointers on some common styling problems, you may be interested in the following suggestions. These all are cohesive, and fit with the **Hybrid Scoped + Utility** philosophy: - -1. Split your app into Layout Components and Base Components -1. Avoid Flexbox and Grid libraries (write your own!) -1. Avoid `margin` on a component wrapper -1. Avoid global media queries - -#### Suggestion #1: Split your app into Layout Components and Base Components - -While this guide will never be long enough to answer the question _"How should a page be laid out?"_ (that's a [design problem!][cassie-evans-css]) there is a more specific question hiding within that we _can_ answer: _"Given a layout, how should components/styles be organized?"_ The answer is **don't bake layout into components.** Have layout components that control layout, and base components (buttons, cards, etc.) that don't control layout. _What does that mean?_ Let's walk through an example so it's more clear. Pretend we have a page that looks like this (numbers for different components): - -``` -|---------------| -| 1 | -|-------+-------| -| 2 | 2 | -|---+---|---+---| -| 3 | 3 | 3 | 3 | -|---+---+---+---| -| 3 | 3 | 3 | 3 | -|---+---+---+---| -``` - -The layout consists of a big, giant, full-width post at top, followed by two half-width posts below it. And below that, we want a bunch of smaller posts to fill out the rest of the page. For simplicity, we'll just call these `<BigPost>` (1), `<MediumPost>` (2), and `<SmallPost>` (3). We add them to our page like so: - -```astro ---- -// src/pages/index.astro - -import Nav from '../components/Nav.astro'; -import BigPost from '../components/BigPost.astro'; -import Grid from '../components/Grid.astro'; -import MediumPosts from '../components/MediumPosts.astro'; -import SmallPosts from '../components/SmallPosts.astro'; -import Footer from '../components/Footer.astro'; ---- -<html> - <body> - <Nav /> - - <Grid> - <BigPost /> - <MediumPosts /> - <SmallPosts /> - </Grid> - - <Footer /> - </body> -</html> -``` - -This _looks_ clean, but looks can be deceiving. At first glance, we may think that `<Grid>` is controlling the layout, but that's an illusion. We actually have `<BigPost>` handling its own width, `<MediumPosts>` loading 2 components and controlling its width, and `<SmallPosts>` loading 4+ components and controlling its width. In total, including `<Grid>`, that means **4 components** are all fighting over the same layout. Remove one post from `<MediumPosts>`, the layout breaks. Edit `<BigPost>`, the layout breaks. Edit `<Grid>`, the layout breaks. If you think about it, none of these components are truly reusable—they might as well just be one big file. - -This is actually the **Global CSS Problem** in disguise—multiple components fight over how they all lay out together, without layout being one, central responsibility (kinda like global CSS)! Now that we identified the problem, one way to fix this is to hoist the entire layout to the top level, and load all components there, too: - -```astro ---- -// src/pages/index.astro - -import Nav from '../components/Nav.astro'; -import BigPost from '../components/BigPost.astro'; -import MediumPost from '../components/MediumPost.astro'; -import SmallPost from '../components/SmallPost.astro'; -import Footer from '../components/Footer.astro'; ---- - -<html> - <head> - <style lang="scss"> - .wrapper { - max-width: 60rem; - margin-right: auto; - margin-left: auto; - padding-right: 2rem; - padding-left: 2rem; - } - - .grid { - display: grid; - grid-gap: 1.5rem; - grid-template columns: 1fr 1fr 1fr 1fr; - } - - .big-post { - grid-column: span 4; - } - - .medium-post { - grid-column: span 2; - } - - .small-post { - grid-column: span 1; - } - </style> - </head> - <body> - <Nav /> - - <div class="wrapper"> - <div class="grid"> - <div class="big-post"><BigPost postId={12345} /></div> - - <div class="medium-post"><MediumPost postId={12345} /></div> - <div class="medium-post"><MediumPost postId={12345} /></div> - - <div class="small-post"><SmallPost postId={12345} /></div> - <div class="small-post"><SmallPost postId={12345} /></div> - <div class="small-post"><SmallPost postId={12345} /></div> - <div class="small-post"><SmallPost postId={12345} /></div> - <div class="small-post"><SmallPost postId={12345} /></div> - <div class="small-post"><SmallPost postId={12345} /></div> - <div class="small-post"><SmallPost postId={12345} /></div> - <div class="small-post"><SmallPost postId={12345} /></div> - </div> - </div> - - <Footer /> - </body> -</html> -``` - -Getting over that this is more code, it's actually a much cleaner separation. What was a four-component layout is now managed 100% within the top-level `index.astro` (which we can now consider a **Layout Component**, and if we wanted to reuse this we could extract this into its own file). Your layout is centralized, and now these components truly are reusable because they don't care one bit about whether they're in the same grid or not. You can edit styles in any of these files now without fear of styles breaking in another. - -The basic rule is that when orchestrating multiple components, **that's a unique responsibility** that should live in one central place, rather than split between 4 components as we were doing. In fact, top-level pages are great at this, and should always be the starting point of your layout components. See how far you can take it, and only extract layout components when you absolutely have to. - -To recap: **if you have to touch multiple files to manage one layout, you probably need to reorganize everything into a Layout Component.** - -💁 **Why this works well in Astro**: In Astro, anything can be a `.astro` component, and you never incur performance problems no matter how many components you add. But the main benefit to [Layout isolation][layout-isolated] is how much it cuts down on the amount of CSS you need. - -#### Suggestion #2: Avoid Flexbox and Grid libraries (write your own!) - -This may feel like a complete overreach to tell you not to use your favorite layout framework you're familiar with. After all, it's gotten you this far! But the days of [float madness](https://zellwk.com/blog/responsive-grid-system/) are gone, replaced by Flexbox and Grid. And the latter don't need libraries to manage them (often they can make it harder). - -Many front-end developers experience the following train of thought: - -1. I should reuse as much CSS as possible (_good!_) -2. Many pages reuse the same layout, … (_hold up—_) -3. … therefore I can find an existing solution to manage all my duplicate layouts (_wait a minute—_) - -While the logic is sound, the reality is that #2 isn't truth for many projects. Probably, many parts of the website weren't designed to fit into these nice, neat, 12 column grids. Even modest web apps can contain _hundreds_ of unique layouts when you factor in all the breakpoints. Ask yourself: _If the website I'm building really contains so many unique layouts, why am I using a heavy grid library that only gives me generic layouts?_ - -A few well-written lines of CSS Grid here and there will not only be perfect in every occasion; it's likely lighter and easier to manage than that heavy library you've fought with for so long. Another way to look at it: if you have to spend a couple hours learning a proprietary styling framework, wrestling with it, filing issues, etc., why not just spend that time on Flexbox and Grid instead? For many people, learning the basics only takes an hour, and that can get you pretty far! There are great, free, learning resources that are worth your time: - -- [Flexbox Froggy](https://flexboxfroggy.com/) -- [CSS Grid Garden](https://cssgridgarden.com/) - -So in short: stop trying to deduplicate layouts when there's nothing to deduplicate! You'll find your styles not only easier to manage, but your CSS payloads much lighter, and load times faster. - -💁 **Why this works well in Astro**: grid libraries are a quick path to stylesheet bloat, and a major contributor to people attempting to [treeshake their styles][css-treeshaking]. Astro does **not** treeshake unused CSS for you, because [that can cause problems][css-treeshaking]. We're not saying you have to be library free; we're big fans of libraries like [Material UI][material-ui]. But if you can at least shed the thousands upon thousands of layouts you're not using from your styling library, you probably don't need automatic treeshaking. - -#### Suggestion #3: Avoid `margin` on a component wrapper - -In other words, don't do this: - -```astro -<!-- src/components/MyComponent.astro --> -<style lang="scss"> - .wrapper { - /* ❌ Don't do this! */ - margin-top: 3rem; - } -</style> - -<div class="wrapper"></div> -``` - -If you remember the [CSS box model][box-model], `margin` extends beyond the boundaries of the box. This means that when you place `margin` on the outermost element, now that will push other components next to it. Even though the styles are scoped, it's _technically_ affecting elements around it, so it [breaks the concept of style containment][layout-isolated]. - -When you have components that rearrange, or appear different when they're next to other components, that's a hard battle to win. **Components should look and act the same no matter where they are placed.** That's what makes them components! - -💁 **Why this works well in Astro**: margins pushing other components around creeps into your styling architecture in sneaky ways, and can result in the creation of some wonky or brittle layout components. Avoiding it altogether will keep your layout components simpler, and you'll spend less time styling in general. - -#### Suggestion #4: Avoid global media queries - -The final point is a natural boundary of **Scoped Styles**. That extends to breakpoints, too! You know that one, weird breakpoint where your `<Card />` component wraps awkwardly at a certain size? You should handle that within `<Card />`, and not anywhere else. - -Even if you end up with some random value like `@media (min-width: 732px) {`, that'll probably work better than trying to create a global [magic number][magic-number] somewhere that only applies to one context (an arbitrary value may be "magic" to the rest of an app, but it does still have meaning within the context of a component that needs that specific value). - -Granted, this has been near-impossible to achieve until Container Queries; fortunately [they are finally landing!][container-queries] - -Also, a common complaint of this approach is when someone asks _"What if I have 2 components that need to do the same thing at the same breakpoint?"_ to which my answer is: you'll always have one or two of those; just handle those as edge cases. But if your entire app is made up of dozens of these cases, perhaps your component lines could be redrawn so that they're more [layout-isolated][layout-isolated] in general. - -💁 **Why this works well in Astro**: this is probably the least important point, which is why it's saved for last. In fact, you could probably skip this if it doesn't work for you. But it's something that people try to architect for at scale, and having a global system to manage this can often be unnecessary. Give _not_ architecting for global media queries a try, and see how far it takes you! - -### 👓 Further Reading - -This guide wouldn't be possible without the following blog posts, which expand on these topics and explain them in more detail. Please give them a read! - -- [**Layout-isolated Components**][layout-isolated] by Emil Sjölander -- [**In defense of utility-first CSS**][utility-css] by Sarah Dayan - -Also please check out the [Stylelint][stylelint] project to whip your styles into shape. You lint your JS, why not your CSS? - -[autoprefixer]: https://github.com/postcss/autoprefixer -[astro-component]: /en/core-concepts/astro-components#css-styles -[astro-resolve]: /en/reference/api-reference#astroresolve -[bem]: http://getbem.com/introduction/ -[box-model]: https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/The_box_model -[browserslist]: https://github.com/browserslist/browserslist -[browserslist-defaults]: https://github.com/browserslist/browserslist#queries -[cassie-evans-css]: https://twitter.com/cassiecodes/status/1392756828786790400?s=20 -[container-queries]: https://ishadeed.com/article/say-hello-to-css-container-queries/ -[css-modules]: https://github.com/css-modules/css-modules -[css-treeshaking]: https://css-tricks.com/how-do-you-remove-unused-css-from-a-site/ -[fouc]: https://en.wikipedia.org/wiki/Flash_of_unstyled_content -[layout-isolated]: https://web.archive.org/web/20210227162315/https://visly.app/blogposts/layout-isolated-components -[less]: https://lesscss.org/ -[issues]: https://github.com/withastro/astro/issues -[magic-number]: https://css-tricks.com/magic-numbers-in-css/ -[material-ui]: https://material.io/components -[peace-on-css]: https://didoo.medium.com/let-there-be-peace-on-css-8b26829f1be0 -[sass]: https://sass-lang.com/ -[sass-use]: https://sass-lang.com/documentation/at-rules/use -[smacss]: http://smacss.com/ -[styled-components]: https://styled-components.com/ -[stylus]: https://stylus-lang.com/ -[styled-jsx]: https://github.com/vercel/styled-jsx -[stylelint]: https://stylelint.io/ -[svelte-style]: https://svelte.dev/docs#style -[tailwind]: https://tailwindcss.com -[tailwind-utilities]: https://tailwindcss.com/docs/adding-new-utilities#using-css -[utility-css]: https://frontstuff.io/in-defense-of-utility-first-css -[vite-preprocessors]: https://vitejs.dev/guide/features.html#css-pre-processors -[vue-css-modules]: https://vue-loader.vuejs.org/guide/css-modules.html -[vue-scoped]: https://vue-loader.vuejs.org/guide/scoped-css.html |