@spree/docs 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -0
- package/dist/api-reference/platform/authentication.md +38 -0
- package/dist/api-reference/store-api/authentication.md +188 -0
- package/dist/api-reference/store-api/errors.md +277 -0
- package/dist/api-reference/store-api/idempotency.md +129 -0
- package/dist/api-reference/store-api/introduction.md +34 -0
- package/dist/api-reference/store-api/localization.md +279 -0
- package/dist/api-reference/store-api/metadata.md +160 -0
- package/dist/api-reference/store-api/monetary-amounts.md +65 -0
- package/dist/api-reference/store-api/querying.md +399 -0
- package/dist/api-reference/store-api/rate-limitting.md +103 -0
- package/dist/api-reference/store-api/relations.md +185 -0
- package/dist/api-reference/storefront/authentication.md +88 -0
- package/dist/api-reference/tutorials/adyen-integration-guide-for-android.md +165 -0
- package/dist/api-reference/tutorials/adyen-integration-guide-for-ios.md +194 -0
- package/dist/api-reference/tutorials/quick-checkout-with-stripe.md +248 -0
- package/dist/api-reference/v2/fetching-multiple-resources.md +26 -0
- package/dist/api-reference/v2/filtering-and-sorting.md +53 -0
- package/dist/api-reference/v2/introduction.md +22 -0
- package/dist/api-reference/v2/pagination.md +37 -0
- package/dist/api-reference/webhooks-events.md +883 -0
- package/dist/developer/admin/admin.md +205 -0
- package/dist/developer/admin/authentication.md +59 -0
- package/dist/developer/admin/components.md +711 -0
- package/dist/developer/admin/custom-css.md +243 -0
- package/dist/developer/admin/custom-javascript.md +116 -0
- package/dist/developer/admin/extending-ui.md +1964 -0
- package/dist/developer/admin/form-builder.md +444 -0
- package/dist/developer/admin/helper-methods.md +531 -0
- package/dist/developer/admin/navigation.md +805 -0
- package/dist/developer/admin/tables.md +491 -0
- package/dist/developer/advanced/adding_spree_to_rails_app.md +106 -0
- package/dist/developer/cli/quickstart.md +137 -0
- package/dist/developer/contributing/creating-an-extension.md +258 -0
- package/dist/developer/contributing/developing-spree.md +339 -0
- package/dist/developer/contributing/quickstart.md +32 -0
- package/dist/developer/contributing/updating-extensions.md +67 -0
- package/dist/developer/core-concepts/addresses.md +265 -0
- package/dist/developer/core-concepts/adjustments.md +107 -0
- package/dist/developer/core-concepts/architecture.md +177 -0
- package/dist/developer/core-concepts/calculators.md +323 -0
- package/dist/developer/core-concepts/customers.md +230 -0
- package/dist/developer/core-concepts/events.md +624 -0
- package/dist/developer/core-concepts/imports-exports.md +698 -0
- package/dist/developer/core-concepts/inventory.md +191 -0
- package/dist/developer/core-concepts/markets.md +250 -0
- package/dist/developer/core-concepts/media.md +167 -0
- package/dist/developer/core-concepts/metafields.md +187 -0
- package/dist/developer/core-concepts/orders.md +328 -0
- package/dist/developer/core-concepts/payments.md +710 -0
- package/dist/developer/core-concepts/pricing.md +163 -0
- package/dist/developer/core-concepts/products.md +360 -0
- package/dist/developer/core-concepts/promotions.md +322 -0
- package/dist/developer/core-concepts/reports.md +206 -0
- package/dist/developer/core-concepts/search-filtering.md +237 -0
- package/dist/developer/core-concepts/shipments.md +212 -0
- package/dist/developer/core-concepts/slugs.md +111 -0
- package/dist/developer/core-concepts/staff-roles.md +123 -0
- package/dist/developer/core-concepts/store-credits-gift-cards.md +317 -0
- package/dist/developer/core-concepts/stores.md +117 -0
- package/dist/developer/core-concepts/taxes.md +135 -0
- package/dist/developer/core-concepts/translations.md +120 -0
- package/dist/developer/core-concepts/users.md +299 -0
- package/dist/developer/core-concepts/webhooks.md +378 -0
- package/dist/developer/create-spree-app/quickstart.md +158 -0
- package/dist/developer/customization/api.md +93 -0
- package/dist/developer/customization/authentication.md +88 -0
- package/dist/developer/customization/checkout.md +204 -0
- package/dist/developer/customization/configuration.md +55 -0
- package/dist/developer/customization/decorators.md +523 -0
- package/dist/developer/customization/dependencies.md +232 -0
- package/dist/developer/customization/emails.md +21 -0
- package/dist/developer/customization/extensions.md +92 -0
- package/dist/developer/customization/metadata.md +236 -0
- package/dist/developer/customization/model-preferences.md +130 -0
- package/dist/developer/customization/permissions.md +265 -0
- package/dist/developer/customization/quickstart.md +229 -0
- package/dist/developer/customization/routes.md +24 -0
- package/dist/developer/customization/v4/admin-panel.md +78 -0
- package/dist/developer/customization/v4/authentication.md +210 -0
- package/dist/developer/customization/v4/checkout.md +212 -0
- package/dist/developer/customization/v4/deface.md +251 -0
- package/dist/developer/customization/v4/images.md +86 -0
- package/dist/developer/customization/v4/storefront.md +450 -0
- package/dist/developer/deployment/assets.md +87 -0
- package/dist/developer/deployment/aws.md +335 -0
- package/dist/developer/deployment/caching.md +27 -0
- package/dist/developer/deployment/cdn.md +39 -0
- package/dist/developer/deployment/database.md +155 -0
- package/dist/developer/deployment/docker.md +128 -0
- package/dist/developer/deployment/emails.md +77 -0
- package/dist/developer/deployment/environment_variables.md +111 -0
- package/dist/developer/deployment/heroku.md +51 -0
- package/dist/developer/deployment/render.md +95 -0
- package/dist/developer/getting-started/quickstart.md +82 -0
- package/dist/developer/how-to/custom-payment-method.md +374 -0
- package/dist/developer/how-to/custom-promotion.md +373 -0
- package/dist/developer/how-to/custom-report.md +387 -0
- package/dist/developer/how-to/custom-search-provider.md +230 -0
- package/dist/developer/multi-store/quickstart.md +71 -0
- package/dist/developer/multi-store/setup.md +38 -0
- package/dist/developer/multi-tenant/configuration.md +41 -0
- package/dist/developer/multi-tenant/core-concepts.md +75 -0
- package/dist/developer/multi-tenant/installation.md +96 -0
- package/dist/developer/multi-tenant/quickstart.md +20 -0
- package/dist/developer/multi-vendor/installation.md +45 -0
- package/dist/developer/multi-vendor/quickstart.md +17 -0
- package/dist/developer/sdk/admin/quickstart.md +22 -0
- package/dist/developer/sdk/authentication.md +89 -0
- package/dist/developer/sdk/configuration.md +225 -0
- package/dist/developer/sdk/quickstart.md +82 -0
- package/dist/developer/sdk/store/account.md +67 -0
- package/dist/developer/sdk/store/cart-checkout.md +140 -0
- package/dist/developer/sdk/store/markets.md +151 -0
- package/dist/developer/sdk/store/payments.md +96 -0
- package/dist/developer/sdk/store/products.md +149 -0
- package/dist/developer/sdk/store/wishlists.md +52 -0
- package/dist/developer/security/pci_compliance.md +15 -0
- package/dist/developer/security/security_policy.md +68 -0
- package/dist/developer/storefront/blocks.md +285 -0
- package/dist/developer/storefront/custom-css.md +260 -0
- package/dist/developer/storefront/custom-javascript.md +166 -0
- package/dist/developer/storefront/helper-methods.md +1288 -0
- package/dist/developer/storefront/links.md +298 -0
- package/dist/developer/storefront/nextjs/architecture.md +150 -0
- package/dist/developer/storefront/nextjs/customization.md +141 -0
- package/dist/developer/storefront/nextjs/deployment.md +180 -0
- package/dist/developer/storefront/nextjs/quickstart.md +92 -0
- package/dist/developer/storefront/nextjs/spree-next-package.md +314 -0
- package/dist/developer/storefront/pages.md +163 -0
- package/dist/developer/storefront/sections.md +569 -0
- package/dist/developer/storefront/storefront.md +56 -0
- package/dist/developer/storefront/themes.md +161 -0
- package/dist/developer/tutorial/admin.md +134 -0
- package/dist/developer/tutorial/extending-models.md +380 -0
- package/dist/developer/tutorial/file-uploads.md +121 -0
- package/dist/developer/tutorial/introduction.md +33 -0
- package/dist/developer/tutorial/model.md +41 -0
- package/dist/developer/tutorial/page-builder.md +487 -0
- package/dist/developer/tutorial/rich-text.md +73 -0
- package/dist/developer/tutorial/seo.md +332 -0
- package/dist/developer/tutorial/storefront.md +352 -0
- package/dist/developer/tutorial/testing.md +558 -0
- package/dist/developer/upgrades/2.0-to-2.1.md +46 -0
- package/dist/developer/upgrades/2.1-to-2.2.md +59 -0
- package/dist/developer/upgrades/2.2-to-2.3.md +44 -0
- package/dist/developer/upgrades/2.3-to-2.4.md +42 -0
- package/dist/developer/upgrades/3.0-to-3.1.md +47 -0
- package/dist/developer/upgrades/3.1-to-3.2.md +34 -0
- package/dist/developer/upgrades/3.2-to-3.3.md +70 -0
- package/dist/developer/upgrades/3.3-to-3.4.md +36 -0
- package/dist/developer/upgrades/3.4-to-3.5.md +44 -0
- package/dist/developer/upgrades/3.5-to-3.6.md +40 -0
- package/dist/developer/upgrades/3.6-to-3.7.md +62 -0
- package/dist/developer/upgrades/3.7-to-4.0.md +152 -0
- package/dist/developer/upgrades/4.0-to-4.1.md +92 -0
- package/dist/developer/upgrades/4.1-to-4.2.md +109 -0
- package/dist/developer/upgrades/4.10-to-5.0.md +129 -0
- package/dist/developer/upgrades/4.2-to-4.3.md +100 -0
- package/dist/developer/upgrades/4.3-to-4.4.md +125 -0
- package/dist/developer/upgrades/4.4-to-4.5.md +94 -0
- package/dist/developer/upgrades/4.5-to-4.6.md +119 -0
- package/dist/developer/upgrades/4.6-to-4.7.md +39 -0
- package/dist/developer/upgrades/4.8-to-4.9.md +24 -0
- package/dist/developer/upgrades/4.9-to-4.10.md +24 -0
- package/dist/developer/upgrades/4.x-to-4.8.md +52 -0
- package/dist/developer/upgrades/5.0-to-5.1.md +28 -0
- package/dist/developer/upgrades/5.1-to-5.2.md +127 -0
- package/dist/developer/upgrades/5.2-to-5.3.md +338 -0
- package/dist/developer/upgrades/5.3-to-5.4.md +248 -0
- package/dist/developer/upgrades/quickstart.md +36 -0
- package/dist/integrations/analytics/google-analytics.md +64 -0
- package/dist/integrations/analytics/google-tag-manager.md +78 -0
- package/dist/integrations/integrations.md +39 -0
- package/dist/integrations/marketing/klaviyo.md +99 -0
- package/dist/integrations/payments/adyen.md +90 -0
- package/dist/integrations/payments/paypal.md +41 -0
- package/dist/integrations/payments/razorpay.md +45 -0
- package/dist/integrations/payments/stripe.md +109 -0
- package/dist/integrations/search/meilisearch.md +236 -0
- package/dist/integrations/sso-mfa-social-login/admin-dashboard.md +57 -0
- package/dist/integrations/sso-mfa-social-login/storefront.md +56 -0
- package/package.json +27 -0
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Links"
|
|
3
|
+
sidebarTitle: "Links"
|
|
4
|
+
description: "Learn how to use links for navigation in your Spree Storefront"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Links are used to create navigation throughout the storefront. They can be assigned to [sections](/developer/storefront/sections) and [blocks](/developer/storefront/blocks), enabling users to navigate to different pages, products, categories, or external URLs.
|
|
8
|
+
|
|
9
|
+
## How Links Work
|
|
10
|
+
|
|
11
|
+
The `Spree::PageLink` model provides a flexible way to link to various content types:
|
|
12
|
+
|
|
13
|
+
- **Internal pages** - Link to Pages, Products, Taxons, Posts
|
|
14
|
+
- **External URLs** - Link to any external website
|
|
15
|
+
- **Special links** - Email (`mailto:`) and phone (`tel:`) links
|
|
16
|
+
|
|
17
|
+
Links are managed through the Page Builder interface, where store staff can select what each link points to.
|
|
18
|
+
|
|
19
|
+
## Link Model
|
|
20
|
+
|
|
21
|
+
The [Spree::PageLink](https://github.com/spree/spree/blob/main/core/app/models/spree/page_link.rb) model has these key attributes:
|
|
22
|
+
|
|
23
|
+
| Attribute | Type | Description |
|
|
24
|
+
|-----------|------|-------------|
|
|
25
|
+
| `label` | String | Display text for the link |
|
|
26
|
+
| `url` | String | Custom URL (for external links) |
|
|
27
|
+
| `linkable` | Polymorphic | Reference to internal content (Page, Product, etc.) |
|
|
28
|
+
| `parent` | Polymorphic | Section or Block the link belongs to |
|
|
29
|
+
| `open_in_new_tab` | Boolean | Whether to open in new browser tab |
|
|
30
|
+
| `position` | Integer | Order when multiple links exist |
|
|
31
|
+
|
|
32
|
+
## Linkable Types
|
|
33
|
+
|
|
34
|
+
Links can point to various Spree models:
|
|
35
|
+
|
|
36
|
+
| Linkable Type | Description |
|
|
37
|
+
|---------------|-------------|
|
|
38
|
+
| `Spree::Page` | Internal pages (Home, Shop All, Custom pages) |
|
|
39
|
+
| `Spree::Product` | Product detail pages |
|
|
40
|
+
| `Spree::Taxon` | Category/collection pages |
|
|
41
|
+
| `Spree::Post` | Blog posts |
|
|
42
|
+
| Custom URL | Any external or internal URL |
|
|
43
|
+
|
|
44
|
+
## Using Links in Sections
|
|
45
|
+
|
|
46
|
+
### Single Link
|
|
47
|
+
|
|
48
|
+
For sections that need one link (like a banner):
|
|
49
|
+
|
|
50
|
+
```ruby app/models/spree/page_sections/promo_banner.rb
|
|
51
|
+
module Spree
|
|
52
|
+
module PageSections
|
|
53
|
+
class PromoBanner < Spree::PageSection
|
|
54
|
+
has_one :link, ->(ps) { ps.links },
|
|
55
|
+
class_name: 'Spree::PageLink',
|
|
56
|
+
as: :parent,
|
|
57
|
+
dependent: :destroy,
|
|
58
|
+
inverse_of: :parent
|
|
59
|
+
accepts_nested_attributes_for :link
|
|
60
|
+
|
|
61
|
+
def default_links
|
|
62
|
+
@default_links.presence || [
|
|
63
|
+
Spree::PageLink.new(label: Spree.t(:shop_now))
|
|
64
|
+
]
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Multiple Links
|
|
72
|
+
|
|
73
|
+
For sections that need multiple links (like navigation):
|
|
74
|
+
|
|
75
|
+
```ruby app/models/spree/page_sections/footer.rb
|
|
76
|
+
module Spree
|
|
77
|
+
module PageSections
|
|
78
|
+
class Footer < Spree::PageSection
|
|
79
|
+
# Links are provided by Spree::HasPageLinks concern
|
|
80
|
+
# which is included in Spree::PageSection by default
|
|
81
|
+
|
|
82
|
+
def default_links
|
|
83
|
+
@default_links.presence || [
|
|
84
|
+
Spree::PageLink.new(label: 'About Us'),
|
|
85
|
+
Spree::PageLink.new(label: 'Contact'),
|
|
86
|
+
Spree::PageLink.new(label: 'Privacy Policy')
|
|
87
|
+
]
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Using Links in Blocks
|
|
95
|
+
|
|
96
|
+
Blocks can also have links. Use the `Spree::HasOneLink` concern for single links:
|
|
97
|
+
|
|
98
|
+
```ruby app/models/spree/page_blocks/cta_button.rb
|
|
99
|
+
module Spree
|
|
100
|
+
module PageBlocks
|
|
101
|
+
class CtaButton < Spree::PageBlock
|
|
102
|
+
include Spree::HasOneLink
|
|
103
|
+
|
|
104
|
+
preference :button_style, :string, default: 'primary'
|
|
105
|
+
|
|
106
|
+
# Called when the link is deleted
|
|
107
|
+
def link_destroyed(_link)
|
|
108
|
+
destroy if page_links_count.zero?
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Accessing Links
|
|
116
|
+
|
|
117
|
+
```ruby
|
|
118
|
+
# Single link on a section
|
|
119
|
+
section.link
|
|
120
|
+
section.link.label
|
|
121
|
+
section.link.linkable_url
|
|
122
|
+
|
|
123
|
+
# Multiple links on a section
|
|
124
|
+
section.links
|
|
125
|
+
section.links.each do |link|
|
|
126
|
+
link.label
|
|
127
|
+
link.linkable_url
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Link on a block
|
|
131
|
+
block.link
|
|
132
|
+
block.link.label if block.link.present?
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Rendering Links
|
|
136
|
+
|
|
137
|
+
### Using the Helper
|
|
138
|
+
|
|
139
|
+
The `page_builder_link_to` helper renders links with Page Builder support:
|
|
140
|
+
|
|
141
|
+
```erb
|
|
142
|
+
<%# Basic usage %>
|
|
143
|
+
<%= page_builder_link_to section.link %>
|
|
144
|
+
|
|
145
|
+
<%# With custom label %>
|
|
146
|
+
<%= page_builder_link_to section.link, label: 'Click Here' %>
|
|
147
|
+
|
|
148
|
+
<%# With CSS class %>
|
|
149
|
+
<%= page_builder_link_to section.link, class: 'btn-primary' %>
|
|
150
|
+
|
|
151
|
+
<%# Open in new tab %>
|
|
152
|
+
<%= page_builder_link_to section.link,
|
|
153
|
+
target: (section.link.open_in_new_tab ? '_blank' : nil),
|
|
154
|
+
rel: (section.link.open_in_new_tab ? 'noopener noreferrer' : nil) %>
|
|
155
|
+
|
|
156
|
+
<%# With block content %>
|
|
157
|
+
<%= page_builder_link_to section.link do %>
|
|
158
|
+
<span class="icon">→</span>
|
|
159
|
+
<%= section.link.label %>
|
|
160
|
+
<% end %>
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Manual Link Rendering
|
|
164
|
+
|
|
165
|
+
For more control, you can render links manually:
|
|
166
|
+
|
|
167
|
+
```erb
|
|
168
|
+
<% if section.link.present? %>
|
|
169
|
+
<%= link_to section.link.linkable_url,
|
|
170
|
+
target: (section.link.open_in_new_tab ? '_blank' : nil),
|
|
171
|
+
rel: (section.link.open_in_new_tab ? 'noopener noreferrer' : nil),
|
|
172
|
+
class: 'btn-primary' do %>
|
|
173
|
+
<%= section.link.label %>
|
|
174
|
+
<% end %>
|
|
175
|
+
<% end %>
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Rendering Multiple Links
|
|
179
|
+
|
|
180
|
+
```erb
|
|
181
|
+
<nav class="footer-links">
|
|
182
|
+
<% section.links.each do |link| %>
|
|
183
|
+
<%= page_builder_link_to link, class: 'footer-link' %>
|
|
184
|
+
<% end %>
|
|
185
|
+
</nav>
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Admin Form for Links
|
|
189
|
+
|
|
190
|
+
In your section's admin form, render the link editor:
|
|
191
|
+
|
|
192
|
+
### Single Link
|
|
193
|
+
|
|
194
|
+
```erb app/views/spree/admin/page_sections/forms/_promo_banner.html.erb
|
|
195
|
+
<%= render 'spree/admin/shared/page_section_image', f: f %>
|
|
196
|
+
|
|
197
|
+
<div class="py-2">
|
|
198
|
+
<%= f.fields_for :link do |lf| %>
|
|
199
|
+
<div class="form-group">
|
|
200
|
+
<%= lf.label :linkable_type, Spree.t(:link) %>
|
|
201
|
+
<%= lf.select :linkable_type,
|
|
202
|
+
@page_section.allowed_linkable_types,
|
|
203
|
+
{ include_blank: false },
|
|
204
|
+
{ class: 'custom-select mb-3', data: { action: 'auto-submit#submit' } } %>
|
|
205
|
+
|
|
206
|
+
<div id="linkable_type_dropdown">
|
|
207
|
+
<%= render 'spree/admin/page_links/linkable_type_dropdown',
|
|
208
|
+
page_link: lf.object,
|
|
209
|
+
form_name: 'page_section[link_attributes]' %>
|
|
210
|
+
</div>
|
|
211
|
+
</div>
|
|
212
|
+
|
|
213
|
+
<div class="custom-control custom-checkbox">
|
|
214
|
+
<%= lf.check_box :open_in_new_tab,
|
|
215
|
+
class: 'custom-control-input',
|
|
216
|
+
data: { action: 'auto-submit#submit' } %>
|
|
217
|
+
<%= lf.label :open_in_new_tab, class: 'custom-control-label' %>
|
|
218
|
+
</div>
|
|
219
|
+
<% end %>
|
|
220
|
+
</div>
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## Link URL Resolution
|
|
224
|
+
|
|
225
|
+
The `linkable_url` method returns the appropriate URL:
|
|
226
|
+
|
|
227
|
+
```ruby
|
|
228
|
+
link = Spree::PageLink.new(linkable: product)
|
|
229
|
+
link.linkable_url
|
|
230
|
+
# => "/products/red-shirt"
|
|
231
|
+
|
|
232
|
+
link = Spree::PageLink.new(url: "https://example.com")
|
|
233
|
+
link.linkable_url
|
|
234
|
+
# => "https://example.com"
|
|
235
|
+
|
|
236
|
+
link = Spree::PageLink.new(url: "example.com")
|
|
237
|
+
link.formatted_url
|
|
238
|
+
# => "http://example.com" # Adds protocol automatically
|
|
239
|
+
|
|
240
|
+
link = Spree::PageLink.new(url: "mailto:info@example.com")
|
|
241
|
+
link.formatted_url
|
|
242
|
+
# => "mailto:info@example.com" # Preserves mailto: protocol
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## Creating Links Programmatically
|
|
246
|
+
|
|
247
|
+
```ruby
|
|
248
|
+
# Link to an internal page
|
|
249
|
+
link = Spree::PageLink.create!(
|
|
250
|
+
parent: section,
|
|
251
|
+
label: 'Shop Now',
|
|
252
|
+
linkable: store.pages.find_by(type: 'Spree::Pages::ShopAll')
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
# Link to a product
|
|
256
|
+
link = Spree::PageLink.create!(
|
|
257
|
+
parent: section,
|
|
258
|
+
label: product.name,
|
|
259
|
+
linkable: product
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
# Link to a taxon
|
|
263
|
+
link = Spree::PageLink.create!(
|
|
264
|
+
parent: section,
|
|
265
|
+
label: 'New Arrivals',
|
|
266
|
+
linkable: store.taxons.find_by(name: 'New Arrivals')
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
# External link
|
|
270
|
+
link = Spree::PageLink.create!(
|
|
271
|
+
parent: section,
|
|
272
|
+
label: 'Visit Our Blog',
|
|
273
|
+
url: 'https://blog.example.com',
|
|
274
|
+
open_in_new_tab: true
|
|
275
|
+
)
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
## Automatic Label Setting
|
|
279
|
+
|
|
280
|
+
When a link's `linkable` is set, the label is automatically populated from the linked resource:
|
|
281
|
+
|
|
282
|
+
```ruby
|
|
283
|
+
link = Spree::PageLink.new(linkable: product)
|
|
284
|
+
link.valid?
|
|
285
|
+
link.label
|
|
286
|
+
# => "Red T-Shirt" (from product.name)
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
The label is derived from (in order):
|
|
290
|
+
1. `linkable.title`
|
|
291
|
+
2. `linkable.display_name`
|
|
292
|
+
3. `linkable.name`
|
|
293
|
+
|
|
294
|
+
## Related Documentation
|
|
295
|
+
|
|
296
|
+
- [Sections](/developer/storefront/sections) - Adding links to sections
|
|
297
|
+
- [Blocks](/developer/storefront/blocks) - Adding links to blocks
|
|
298
|
+
- [Pages](/developer/storefront/pages) - Understanding internal page linking
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Architecture
|
|
3
|
+
description: Server-first architecture, project structure, and auth flow
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## Server-First Pattern
|
|
7
|
+
|
|
8
|
+
The storefront follows a **server-first architecture** where all API calls are made server-side. The Spree API key is never exposed to the browser.
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
Browser → Server Action → @spree/next → Spree API
|
|
12
|
+
(with httpOnly cookies)
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
- **Server Actions** (`src/lib/data/`) — thin wrappers around `@spree/next` data functions
|
|
16
|
+
- **httpOnly Cookies** — auth tokens, cart tokens, and locale are stored securely
|
|
17
|
+
- **No Client-Side API Calls** — the Spree API key stays on the server
|
|
18
|
+
- **Auto-Localization** — locale and country are read from cookies automatically by `@spree/next`
|
|
19
|
+
|
|
20
|
+
## Project Structure
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
src/
|
|
24
|
+
├── app/
|
|
25
|
+
│ └── [country]/[locale]/ # Localized routes
|
|
26
|
+
│ ├── (storefront)/ # Main storefront layout
|
|
27
|
+
│ │ ├── page.tsx # Homepage
|
|
28
|
+
│ │ ├── account/ # Customer account
|
|
29
|
+
│ │ │ ├── addresses/ # Address management
|
|
30
|
+
│ │ │ ├── credit-cards/ # Saved payment methods
|
|
31
|
+
│ │ │ ├── gift-cards/ # Gift cards
|
|
32
|
+
│ │ │ ├── orders/ # Order history
|
|
33
|
+
│ │ │ │ └── [id]/ # Order details
|
|
34
|
+
│ │ │ ├── profile/ # Profile settings
|
|
35
|
+
│ │ │ └── register/ # Registration
|
|
36
|
+
│ │ ├── cart/ # Shopping cart
|
|
37
|
+
│ │ ├── products/ # Product listing
|
|
38
|
+
│ │ │ └── [slug]/ # Product details
|
|
39
|
+
│ │ ├── t/[...permalink]/ # Category pages
|
|
40
|
+
│ │ └── categories/ # Category overview
|
|
41
|
+
│ └── (checkout)/ # Checkout layout (no header/footer)
|
|
42
|
+
│ ├── checkout/[id]/ # Checkout flow
|
|
43
|
+
│ └── order-placed/[id]/ # Order confirmation
|
|
44
|
+
├── components/
|
|
45
|
+
│ ├── cart/ # CartDrawer
|
|
46
|
+
│ ├── checkout/ # AddressStep, DeliveryStep, PaymentStep, etc.
|
|
47
|
+
│ ├── layout/ # Header, Footer, CountrySwitcher
|
|
48
|
+
│ ├── navigation/ # Breadcrumbs
|
|
49
|
+
│ ├── products/ # ProductCard, ProductGrid, Filters, MediaGallery, VariantPicker
|
|
50
|
+
│ └── search/ # SearchBar
|
|
51
|
+
├── contexts/
|
|
52
|
+
│ ├── AuthContext.tsx # Auth state
|
|
53
|
+
│ ├── CartContext.tsx # Client-side cart state sync
|
|
54
|
+
│ ├── CheckoutContext.tsx # Checkout flow state
|
|
55
|
+
│ └── StoreContext.tsx # Store/locale/currency state
|
|
56
|
+
├── hooks/
|
|
57
|
+
│ ├── useCarouselProducts.ts # Product carousel data
|
|
58
|
+
│ └── useProductListing.ts # Product listing with filters
|
|
59
|
+
└── lib/
|
|
60
|
+
├── analytics/ # GTM integration
|
|
61
|
+
├── constants.ts # App constants
|
|
62
|
+
├── data/ # Server Actions
|
|
63
|
+
│ ├── addresses.ts # Address CRUD
|
|
64
|
+
│ ├── cart.ts # Cart operations
|
|
65
|
+
│ ├── checkout.ts # Checkout flow
|
|
66
|
+
│ ├── cookies.ts # Auth check helper
|
|
67
|
+
│ ├── countries.ts # Countries/regions
|
|
68
|
+
│ ├── credit-cards.ts # Payment methods
|
|
69
|
+
│ ├── customer.ts # Auth & profile
|
|
70
|
+
│ ├── gift-cards.ts # Gift cards
|
|
71
|
+
│ ├── orders.ts # Order history
|
|
72
|
+
│ ├── payment.ts # Payment processing
|
|
73
|
+
│ ├── products.ts # Product queries
|
|
74
|
+
│ ├── store.ts # Store configuration
|
|
75
|
+
│ ├── categories.ts # Categories
|
|
76
|
+
│ └── utils.ts # Shared helpers (actionResult, withFallback)
|
|
77
|
+
└── utils/ # Client utilities
|
|
78
|
+
├── address.ts # Address formatting
|
|
79
|
+
├── cookies.ts # Cookie helpers
|
|
80
|
+
├── credit-card.ts # Card formatting
|
|
81
|
+
├── path.ts # URL path helpers
|
|
82
|
+
└── product-query.ts # Product filter query builder
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Authentication Flow
|
|
86
|
+
|
|
87
|
+
1. User submits login form
|
|
88
|
+
2. Server action calls `@spree/next` which authenticates with the Spree API
|
|
89
|
+
3. JWT token is stored in an httpOnly cookie by `@spree/next`
|
|
90
|
+
4. Subsequent requests include the token automatically
|
|
91
|
+
5. Token is never accessible to client-side JavaScript
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
// src/lib/data/customer.ts
|
|
95
|
+
import { login as _login, getCustomer as _getCustomer } from '@spree/next'
|
|
96
|
+
|
|
97
|
+
export async function login(email: string, password: string) {
|
|
98
|
+
return _login(email, password) // token stored in httpOnly cookie automatically
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export async function getCustomer() {
|
|
102
|
+
return _getCustomer() // reads token from cookie automatically
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Multi-Region Support
|
|
107
|
+
|
|
108
|
+
The storefront supports multiple countries and currencies via URL segments:
|
|
109
|
+
|
|
110
|
+
```
|
|
111
|
+
/us/en/products # US store, English
|
|
112
|
+
/de/de/products # German store, German
|
|
113
|
+
/uk/en/products # UK store, English
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
A middleware (`src/proxy.ts`) uses `createSpreeMiddleware` from `@spree/next` to detect the visitor's country and locale, then redirects to the correct URL prefix. The `CountrySwitcher` component lets users change regions manually.
|
|
117
|
+
|
|
118
|
+
## Server Actions
|
|
119
|
+
|
|
120
|
+
All data fetching is done through server actions in `src/lib/data/`. These are thin wrappers around `@spree/next` functions — locale and currency are resolved automatically from cookies:
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
// Products
|
|
124
|
+
import { getProducts, getProduct, getProductFilters } from '@/lib/data/products'
|
|
125
|
+
|
|
126
|
+
const products = await getProducts({ limit: 12 })
|
|
127
|
+
const product = await getProduct('product-slug')
|
|
128
|
+
const filters = await getProductFilters()
|
|
129
|
+
|
|
130
|
+
// Cart
|
|
131
|
+
import { getCart, addToCart, updateCartItem, removeCartItem } from '@/lib/data/cart'
|
|
132
|
+
|
|
133
|
+
const cart = await getCart()
|
|
134
|
+
await addToCart('var_xxx', 1)
|
|
135
|
+
await updateCartItem('li_xxx', 2)
|
|
136
|
+
await removeCartItem('li_xxx')
|
|
137
|
+
|
|
138
|
+
// Authentication
|
|
139
|
+
import { login, register, logout, getCustomer } from '@/lib/data/customer'
|
|
140
|
+
|
|
141
|
+
await login('user@example.com', 'password')
|
|
142
|
+
const customer = await getCustomer()
|
|
143
|
+
await logout()
|
|
144
|
+
|
|
145
|
+
// Addresses
|
|
146
|
+
import { getAddresses, createAddress, updateAddress, deleteAddress } from '@/lib/data/addresses'
|
|
147
|
+
|
|
148
|
+
const addresses = await getAddresses()
|
|
149
|
+
await createAddress({ first_name: 'John', ... })
|
|
150
|
+
```
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Customization
|
|
3
|
+
description: Fork, customize, and extend the Spree Next.js Storefront
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## Forking the Starter
|
|
7
|
+
|
|
8
|
+
The recommended approach is to fork the repository so you can customize freely while pulling upstream updates.
|
|
9
|
+
|
|
10
|
+
### 1. Fork on GitHub
|
|
11
|
+
|
|
12
|
+
Go to [github.com/spree/storefront](https://github.com/spree/storefront) and click **Fork**.
|
|
13
|
+
|
|
14
|
+
### 2. Clone Your Fork
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
git clone https://github.com/YOUR_USERNAME/storefront.git
|
|
18
|
+
cd storefront
|
|
19
|
+
npm install
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### 3. Add Upstream Remote
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
git remote add upstream https://github.com/spree/storefront.git
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### 4. Pull Upstream Updates
|
|
29
|
+
|
|
30
|
+
When the official starter gets updates, pull them into your fork:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
git fetch upstream
|
|
34
|
+
git merge upstream/main
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Resolve any conflicts in your customized files, then commit.
|
|
38
|
+
|
|
39
|
+
## Styling
|
|
40
|
+
|
|
41
|
+
The storefront uses Tailwind CSS 4, which replaces the traditional `tailwind.config.ts` with CSS-native configuration via the `@theme` directive in `src/app/globals.css`.
|
|
42
|
+
|
|
43
|
+
### Theme Customization
|
|
44
|
+
|
|
45
|
+
Edit the `@theme inline` block in `src/app/globals.css` to change colors, fonts, and other design tokens:
|
|
46
|
+
|
|
47
|
+
```css
|
|
48
|
+
@import "tailwindcss";
|
|
49
|
+
|
|
50
|
+
:root {
|
|
51
|
+
--background: #fcfaf7;
|
|
52
|
+
--foreground: #171717;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@theme inline {
|
|
56
|
+
--color-background: var(--background);
|
|
57
|
+
--color-foreground: var(--foreground);
|
|
58
|
+
--font-sans: var(--font-geist);
|
|
59
|
+
|
|
60
|
+
/* Replace with your brand colors */
|
|
61
|
+
--color-primary-50: #eff6ff;
|
|
62
|
+
--color-primary-100: #dbeafe;
|
|
63
|
+
--color-primary-200: #bfdbfe;
|
|
64
|
+
--color-primary-300: #93c5fd;
|
|
65
|
+
--color-primary-400: #60a5fa;
|
|
66
|
+
--color-primary-500: #0077ff;
|
|
67
|
+
--color-primary-600: #0066dd;
|
|
68
|
+
--color-primary-700: #0055bb;
|
|
69
|
+
--color-primary-800: #004499;
|
|
70
|
+
--color-primary-900: #003377;
|
|
71
|
+
--color-primary-950: #001d4d;
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Variables defined in `@theme inline` become Tailwind utilities automatically — for example, `--color-primary-500` maps to `bg-primary-500`, `text-primary-500`, etc.
|
|
76
|
+
|
|
77
|
+
## Components
|
|
78
|
+
|
|
79
|
+
All components live in `src/components/` and can be customized or replaced:
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
src/components/
|
|
83
|
+
├── cart/ # CartDrawer
|
|
84
|
+
├── checkout/ # AddressStep, DeliveryStep, PaymentStep, StripePaymentForm, etc.
|
|
85
|
+
├── layout/ # Header, Footer, CountrySwitcher
|
|
86
|
+
├── navigation/ # Breadcrumbs
|
|
87
|
+
├── products/ # ProductCard, ProductGrid, ProductCarousel, Filters, MediaGallery, VariantPicker
|
|
88
|
+
└── search/ # SearchBar
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Components use standard React patterns — modify them directly or replace them entirely with your own implementations.
|
|
92
|
+
|
|
93
|
+
## Data Layer
|
|
94
|
+
|
|
95
|
+
To customize API behavior, modify the server actions in `src/lib/data/`. Each file handles a specific domain:
|
|
96
|
+
|
|
97
|
+
| File | Purpose |
|
|
98
|
+
|------|---------|
|
|
99
|
+
| `products.ts` | Product listing and detail queries |
|
|
100
|
+
| `cart.ts` | Cart operations (add, update, remove) |
|
|
101
|
+
| `checkout.ts` | Checkout flow (addresses, shipping, completion) |
|
|
102
|
+
| `customer.ts` | Authentication and profile management |
|
|
103
|
+
| `addresses.ts` | Address CRUD |
|
|
104
|
+
| `orders.ts` | Order history |
|
|
105
|
+
| `payment.ts` | Payment sessions and processing |
|
|
106
|
+
| `categories.ts` | Categories |
|
|
107
|
+
| `countries.ts` | Country and region data |
|
|
108
|
+
| `cookies.ts` | Auth check helper |
|
|
109
|
+
| `store.ts` | Store configuration |
|
|
110
|
+
| `credit-cards.ts` | Saved payment methods |
|
|
111
|
+
| `gift-cards.ts` | Gift card management |
|
|
112
|
+
| `utils.ts` | Shared helpers (error handling, fallbacks) |
|
|
113
|
+
|
|
114
|
+
These server actions wrap the `@spree/next` package functions. You can add custom logic, caching strategies, or additional transformations as needed.
|
|
115
|
+
|
|
116
|
+
## Adding New Pages
|
|
117
|
+
|
|
118
|
+
Follow the existing App Router pattern with localized routes. Place pages under the `(storefront)` route group to inherit the shared header/footer layout:
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
src/app/[country]/[locale]/(storefront)/your-new-page/page.tsx
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
import { listProducts } from '@spree/next';
|
|
126
|
+
|
|
127
|
+
export default async function YourNewPage() {
|
|
128
|
+
const products = await listProducts({ limit: 6 });
|
|
129
|
+
|
|
130
|
+
return (
|
|
131
|
+
<div>
|
|
132
|
+
<h1>Your New Page</h1>
|
|
133
|
+
{/* Your content */}
|
|
134
|
+
</div>
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Building a Custom Storefront
|
|
140
|
+
|
|
141
|
+
If you prefer to build from scratch instead of forking the starter, you can use the `@spree/next` and `@spree/sdk` packages directly in any Next.js application. See the [@spree/next Package](/developer/storefront/nextjs/spree-next-package) documentation for the complete API reference.
|