diff --git a/.alexrc.js b/.alexrc.js
index 71913eb..bd676be 100644
--- a/.alexrc.js
+++ b/.alexrc.js
@@ -1,4 +1,13 @@
// Use a "maybe" level of profanity instead of the default "unlikely".
exports.profanitySureness = 1
-exports.allow = ["simple", "special", "invalid", "he-she", "her-him", "herself-himself", "obvious"]
+exports.allow = [
+ "simple",
+ "special",
+ "invalid",
+ "he-she",
+ "her-him",
+ "herself-himself",
+ "obvious",
+ "easy",
+]
diff --git a/.prettierrc b/.prettierrc
index 77b066d..abe4813 100644
--- a/.prettierrc
+++ b/.prettierrc
@@ -8,7 +8,9 @@
"files": "*.mdx",
"options": {
"printWidth": 74,
- "proseWrap": "always"
+ "proseWrap": "always",
+ "bracketSpacing": true,
+ "trailingComma": "es5"
}
}
]
diff --git a/README.md b/README.md
index ee04909..f7d345a 100644
--- a/README.md
+++ b/README.md
@@ -70,3 +70,24 @@ Example:
```
If you aren't totally sure how the slug should look like, or just want to automate the process, run `yarn english-slugify`
+
+## Cards
+
+You can use cards, like the one at the start of [_API Routes_](https://blitzjs.com/docs/api-routes) like this:
+
+```md
+
...
, - }, + } ) function Home() { @@ -129,13 +137,13 @@ when the module includes a library that only works in the browser. Take a look at the following example: ```jsx -import {dynamic} from "blitz" +import { dynamic } from "blitz" const DynamicComponentWithNoSSR = dynamic( () => import("../components/hello3"), { ssr: false, - }, + } ) function Home() { diff --git a/app/pages/docs/contributing.mdx b/app/pages/docs/contributing.mdx index 6449a50..56184d6 100644 --- a/app/pages/docs/contributing.mdx +++ b/app/pages/docs/contributing.mdx @@ -149,6 +149,17 @@ Most Blitz packages in `packages/` have jest unit tests. Blitz integration tests are inside the root `test/` folder. +Make sure you have `chromedriver` installed for your Chrome version. You +can install it with + +- `brew install --cask chromedriver` on Mac OS X +- `chocolatey install chromedriver` on Windows +- Or manually download the version that matches your installed chrome + version (if there's no match, download a version under it, but not + above) from the + [chromedriver repo](https://chromedriver.storage.googleapis.com/index.html) + and add the binary to `
{statusCode
@@ -44,9 +44,9 @@ function Error({statusCode}) {
)
}
-Error.getInitialProps = ({res, err}) => {
+Error.getInitialProps = ({ res, err }) => {
const statusCode = res ? res.statusCode : err ? err.statusCode : 404
- return {statusCode}
+ return { statusCode }
}
export default Error
@@ -159,7 +159,7 @@ It accepts two props:
- `title` - a string to display as the error message
```jsx
-import {ErrorComponent} from "blitz"
+import { ErrorComponent } from "blitz"
export default function Page() {
return
{posts.map((post) => (
@@ -297,7 +297,7 @@ import fs from "fs"
import path from "path"
// posts will be populated at build time by getStaticProps()
-function Blog({posts}) {
+function Blog({ posts }) {
return (
{posts.map((post) => (
@@ -376,8 +376,8 @@ the exported JSON is used.
#### Only allowed in a page
-`getStaticProps` can only be exported from a **page**. You can’t export it
-from non-page files.
+`getStaticProps` can only be exported from a [page](./pages). You can’t
+export it from non-page files.
One of the reasons for this restriction is that React needs to have all
the required data before the page is rendered.
@@ -391,6 +391,17 @@ component.
In development (`blitz dev`), `getStaticProps` will be called on every
request.
+#### First Render UX
+
+Even though a page with `getStaticProps` will be pre-rendered on the
+server, it may fallback to the page's loading state if your page contains
+queries that request data missing from the initial page props. This can
+create a flickering effect where the contents of the page rapidly changes
+when the query data gets loaded into the app.
+
+[Click here](./pages#first-render-ux) to discover ways you can improve the
+first render User Experience (UX) of your pages.
+
#### Preview Mode
In some cases, you might want to temporarily bypass Static Generation and
diff --git a/app/pages/docs/head-component.mdx b/app/pages/docs/head-component.mdx
index e82b78b..e1da3a6 100644
--- a/app/pages/docs/head-component.mdx
+++ b/app/pages/docs/head-component.mdx
@@ -7,7 +7,7 @@ We expose a built-in component for appending elements to the `head` of the
page:
```jsx
-import {Head} from "blitz"
+import { Head } from "blitz"
function IndexPage() {
return (
@@ -32,7 +32,7 @@ which will make sure the tag is only rendered once, as in the following
example:
```jsx
-import {Head} from "blitz"
+import { Head } from "blitz"
function IndexPage() {
return (
diff --git a/app/pages/docs/headers.mdx b/app/pages/docs/headers.mdx
new file mode 100644
index 0000000..c9812ab
--- /dev/null
+++ b/app/pages/docs/headers.mdx
@@ -0,0 +1,390 @@
+---
+title: HTTP Headers
+sidebar_label: HTTP Headers
+---
+
+Headers allow you to set custom HTTP headers for an incoming request path.
+
+To set custom HTTP headers you can use the `headers` key in
+`blitz.config.js`:
+
+```js
+module.exports = {
+ async headers() {
+ return [
+ {
+ source: "/about",
+ headers: [
+ {
+ key: "x-custom-header",
+ value: "my custom header value",
+ },
+ {
+ key: "x-another-custom-header",
+ value: "my other custom header value",
+ },
+ ],
+ },
+ ]
+ },
+}
+```
+
+`headers` is an async function that expects an array to be returned
+holding objects with `source` and `headers` properties:
+
+- `source` is the incoming request path pattern.
+- `headers` is an array of header objects with the `key` and `value`
+ properties.
+- `basePath`: `false` or `undefined` - if false the basePath won't be
+ included when matching, can be used for external rewrites only.
+- `locale`: `false` or `undefined` - whether the locale should not be
+ included when matching.
+- `has` is an array of [has objects](#header-cookie-and-query-matching)
+ with the `type`, `key` and `value` properties.
+
+Headers are checked before the filesystem which includes pages and
+`/public` files.
+
+## Header Overriding Behavior {#overriding}
+
+If two headers match the same path and set the same header key, the last
+header key will override the first. Using the below headers, the path
+`/hello` will result in the header `x-hello` being `world` due to the last
+header value set being `world`.
+
+```js
+module.exports = {
+ async headers() {
+ return [
+ {
+ source: '/:path*',
+ headers: [
+ {
+ key: 'x-hello',
+ value: 'there',
+ },
+ ],
+ },
+ {
+ source: '/hello',
+ headers: [
+ {
+ key: 'x-hello',
+ value: 'world',
+ },
+ ],
+ },
+ ],
+ },
+}
+```
+
+## Path Matching {#path-matching}
+
+Path matches are allowed, for example `/blog/:slug` will match
+`/blog/hello-world` (no nested paths):
+
+```js
+module.exports = {
+ async headers() {
+ return [
+ {
+ source: '/blog/:slug',
+ headers: [
+ {
+ key: 'x-slug',
+ value: ':slug', // Matched parameters can be used in the value
+ },
+ {
+ key: 'x-slug-:slug', // Matched parameters can be used in the key
+ value: 'my other custom header value',
+ },
+ ],
+ },
+ ],
+ },
+}
+```
+
+### Wildcard Path Matching {#wildcard}
+
+To match a wildcard path you can use `*` after a parameter, for example
+`/blog/:slug*` will match `/blog/a/b/c/d/hello-world`:
+
+```js
+module.exports = {
+ async headers() {
+ return [
+ {
+ source: '/blog/:slug*',
+ headers: [
+ {
+ key: 'x-slug',
+ value: ':slug*', // Matched parameters can be used in the value
+ },
+ {
+ key: 'x-slug-:slug*', // Matched parameters can be used in the key
+ value: 'my other custom header value',
+ },
+ ],
+ },
+ ],
+ },
+}
+```
+
+### Regex Path Matching {#regex}
+
+To match a regex path you can wrap the regex in parenthesis after a
+parameter, for example `/blog/:slug(\\d{1,})` will match `/blog/123` but
+not `/blog/abc`:
+
+```js
+module.exports = {
+ async headers() {
+ return [
+ {
+ source: '/blog/:post(\\d{1,})',
+ headers: [
+ {
+ key: 'x-post',
+ value: ':post',
+ },
+ ],
+ },
+ ],
+ },
+}
+```
+
+The following characters `(`, `)`, `{`, `}`, `:`, `*`, `+`, `?` are used
+for regex path matching, so when used in the `source` as non-special
+values they must be escaped by adding `\\` before them:
+
+```js
+module.exports = {
+ async redirects() {
+ return [
+ {
+ // this will match `/english(default)/something` being requested
+ source: "/english\\(default\\)/:slug",
+ destination: "/en-us/:slug",
+ permanent: false,
+ },
+ ]
+ },
+}
+```
+
+## Header, Cookie, and Query Matching {#header-cookie-matching}
+
+To only apply a header when either header, cookie, or query values also
+match the `has` field can be used. Both the `source` and all `has` items
+must match for the header to be applied.
+
+`has` items have the following fields:
+
+- `type`: `String` - must be either `header`, `cookie`, `host`, or
+ `query`.
+- `key`: `String` - the key from the selected type to match against.
+- `value`: `String` or `undefined` - the value to check for, if undefined
+ any value will match. A regex like string can be used to capture a
+ specific part of the value, e.g. if the value `first-(?