diff --git a/.changeset/mean-ligers-nail.md b/.changeset/mean-ligers-nail.md
new file mode 100644
index 000000000..68739635f
--- /dev/null
+++ b/.changeset/mean-ligers-nail.md
@@ -0,0 +1,6 @@
+---
+'astro': patch
+'@astrojs/parser': patch
+---
+
+Add support for Fragments with `<>` and `>` syntax
diff --git a/docs/syntax.md b/docs/syntax.md
index edf854046..358644f17 100644
--- a/docs/syntax.md
+++ b/docs/syntax.md
@@ -113,28 +113,52 @@ export let name;
```
+### Fragments
+
+At the top-level of an `.astro` file, you may render any number of elements.
+
+```html
+
+
+
+
+```
+
+Inside of an expression, you must wrap multiple elements in a Fragment. Fragments must open with `<>` and close with `>`.
+
+```jsx
+
+{[0, 1, 2].map(id => (
+ <>
+
+
+
+ >
+))}
+
+```
+
### `.astro` versus `.jsx`
`.astro` files can end up looking very similar to `.jsx` files, but there are a few key differences. Here's a comparison between the two formats.
-| Feature | Astro | JSX |
-| ---------------------------- | ---------------------------------------- | -------------------------------------------------- |
-| File extension | `.astro` | `.jsx` or `.tsx` |
-| User-Defined Components | `` | `` |
-| Expression Syntax | `{}` | `{}` |
-| Spread Attributes | `{...props}` | `{...props}` |
-| Boolean Attributes | `autocomplete` === `autocomplete={true}` | `autocomplete` === `autocomplete={true}` |
-| Inline Functions | `{items.map(item => {item})}` | `{items.map(item => {item})}` |
-| IDE Support | WIP - [VS Code][code-ext] | Phenomenal |
-| Requires JS import | No | Yes, `jsxPragma` (`React` or `h`) must be in scope |
-| Fragments | Automatic | Wrap with `` or `<>` |
-| Multiple frameworks per-file | Yes | No |
-| Modifying `` | Just use `` | Per-framework (``, ``, etc) |
-| Comment Style | `` | `{/* JavaScript */}` |
-| Special Characters | ` ` | `{'\xa0'}` or `{String.fromCharCode(160)}` |
-| Attributes | `dash-case` | `camelCase` |
+| Feature | Astro | JSX |
+| ---------------------------- | -------------------------------------------- | -------------------------------------------------- |
+| File extension | `.astro` | `.jsx` or `.tsx` |
+| User-Defined Components | `` | `` |
+| Expression Syntax | `{}` | `{}` |
+| Spread Attributes | `{...props}` | `{...props}` |
+| Boolean Attributes | `autocomplete` === `autocomplete={true}` | `autocomplete` === `autocomplete={true}` |
+| Inline Functions | `{items.map(item => {item})}` | `{items.map(item => {item})}` |
+| IDE Support | WIP - [VS Code][code-ext] | Phenomenal |
+| Requires JS import | No | Yes, `jsxPragma` (`React` or `h`) must be in scope |
+| Fragments | Automatic top-level, `<>` inside functions | Wrap with `` or `<>` |
+| Multiple frameworks per-file | Yes | No |
+| Modifying `` | Just use `` | Per-framework (``, ``, etc) |
+| Comment Style | `` | `{/* JavaScript */}` |
+| Special Characters | ` ` | `{'\xa0'}` or `{String.fromCharCode(160)}` |
+| Attributes | `dash-case` | `camelCase` |
-### TODO: Styling
### TODO: Composition (Slots)
diff --git a/packages/astro-parser/src/parse/state/tag.ts b/packages/astro-parser/src/parse/state/tag.ts
index b8c3e63ad..28783df67 100644
--- a/packages/astro-parser/src/parse/state/tag.ts
+++ b/packages/astro-parser/src/parse/state/tag.ts
@@ -13,7 +13,7 @@ const valid_tag_name = /^\!?[a-zA-Z]{1,}:?[a-zA-Z0-9\-]*/;
const meta_tags = new Map([
['astro:head', 'Head'],
- // ['slot:body', 'Body'],
+ ['', 'SlotTemplate'],
// ['astro:options', 'Options'],
// ['astro:window', 'Window'],
// ['astro:body', 'Body'],
@@ -118,7 +118,7 @@ export default function tag(parser: Parser) {
? meta_tags.get(name)
: /[A-Z]/.test(name[0]) || name === 'astro:self' || name === 'astro:component'
? 'InlineComponent'
- : name === 'astro:fragment'
+ : name === ''
? 'SlotTemplate'
: name === 'title' && parent_is_head(parser.stack)
? 'Title'
diff --git a/packages/astro/src/compiler/codegen/index.ts b/packages/astro/src/compiler/codegen/index.ts
index 140d3bfc5..b31fbf4a0 100644
--- a/packages/astro/src/compiler/codegen/index.ts
+++ b/packages/astro/src/compiler/codegen/index.ts
@@ -494,6 +494,11 @@ async function compileHtml(enterNode: TemplateNode, state: CodegenState, compile
return;
case 'Fragment':
break;
+ case 'SlotTemplate': {
+ buffers[curr] += `h(Fragment, null, children`;
+ paren++;
+ return;
+ }
case 'Slot':
case 'Head':
case 'InlineComponent': {
@@ -630,6 +635,7 @@ async function compileHtml(enterNode: TemplateNode, state: CodegenState, compile
case 'CodeSpan':
case 'CodeFence':
return;
+ case 'SlotTemplate':
case 'Slot':
case 'Head':
case 'Body':
diff --git a/packages/astro/test/astro-expr.test.js b/packages/astro/test/astro-expr.test.js
index 485d58d9b..727abd1c8 100644
--- a/packages/astro/test/astro-expr.test.js
+++ b/packages/astro/test/astro-expr.test.js
@@ -58,6 +58,17 @@ Expressions('Allows multiple JSX children in mustache', async ({ runtime }) => {
assert.ok(result.contents.includes('#f') && !result.contents.includes('#t'));
});
+Expressions('Allows <> Fragments in expressions', async ({ runtime }) => {
+ const result = await runtime.load('/multiple-children');
+ if (result.error) throw new Error(result.error);
+ const $ = doc(result.contents);
+
+ assert.equal($('#fragment').children().length, 3);
+ assert.equal($('#fragment').children('#a').length, 1);
+ assert.equal($('#fragment').children('#b').length, 1);
+ assert.equal($('#fragment').children('#c').length, 1);
+})
+
Expressions('Does not render falsy values using &&', async ({ runtime }) => {
const result = await runtime.load('/falsy');
if (result.error) throw new Error(result.error);
diff --git a/packages/astro/test/fixtures/astro-expr/src/pages/multiple-children.astro b/packages/astro/test/fixtures/astro-expr/src/pages/multiple-children.astro
index fb0fafd4a..4e0b5e746 100644
--- a/packages/astro/test/fixtures/astro-expr/src/pages/multiple-children.astro
+++ b/packages/astro/test/fixtures/astro-expr/src/pages/multiple-children.astro
@@ -10,5 +10,15 @@ let title = 'My Site';
{title}
{false ? #t
: #f
}
+
+
+ {(
+ <>
+
+
+
+ >
+ )}
+
-
\ No newline at end of file
+