tinybeast-services
v2.0.63
Published
This nuxt theme is suitable for service website
Readme
Tiny Beast theme: services
This nuxt theme is suitable for service website
Setup
Make sure to install the dependencies:
pnpm installWorking on your layer
The layer is at the root of this repository, it is exactly like a regular Nuxt project, except you can publish it on NPM.
The .playground directory should help you on trying your layer during development.
Running pnpm dev will prepare and boot .playground directory, which imports your layer itself.
Distributing your layer
Your Nuxt layer is shaped exactly the same as any other Nuxt project, except you can publish it on NPM.
To do so, you only have to check if files in package.json are valid, then run:
npm publish --access publicOnce done, your users will only have to run:
npm install --save your-layerThen add the dependency to their extends in nuxt.config:
defineNuxtConfig({
extends: 'tinybeast-services'
})Development Server
Start the development server on http://localhost:3000
pnpm devProduction
Build the application for production:
pnpm buildOr statically generate it with:
pnpm generateLocally preview production build:
pnpm previewStoryblok
Storyblok is used for content management. To pull components from Storyblok, run:
storyblok pull-components --space <space_id>storyblok cli 4
storyblok components --space <space_id> pullTo push components to Storyblok, run:
storyblok push-components --space <space_id> <path_to_schema_file>storyblok cli 4
storyblok components push --space <space_id> --from <path_to_schema_file or space_id>Required Environment Variables
BASE_URL=your_base_urlNUXT_PUBLIC_NODE_ENV=your_node_envSTORYBLOK_ACCESS_TOKEN=your_storyblok_access_token
Favicons
Add the following favicon files to the public/ directory (or .playground/public/ for development):
favicon.ico- Standard favicon (16x16 or 32x32)favicon-16x16.png- 16x16 PNG faviconfavicon-32x32.png- 32x32 PNG faviconapple-touch-icon.png- 180x180 PNG for Apple devices
These are configured in nuxt.config.ts and will be automatically included in all pages.
Internationalization (i18n)
This theme supports dynamic locale configuration through environment variables.
Default Behavior
By default, the site runs with Danish only (da) as a single-language website with clean URLs (no language prefixes).
Adding Multiple Languages
To enable multiple languages, set the following environment variables:
# Add multiple languages (JSON format)
NUXT_PUBLIC_I18N_LOCALES='[{"code":"da","name":"Danish","language":"da-DK"},{"code":"en","name":"English","language":"en-US"}]'
# Optionally change the default locale
NUXT_PUBLIC_I18N_DEFAULT_LOCALE='da'Settings in Storyblok
You have to go and enable internationalization in Storyblok. Go to Settings > Internationalization and enable it. If you want AI translation, you can enable it in Settings > Labs > AI Translation.
Translatable Slugs (Field-Based Translation)
When using field-based translation mode (default), you can configure translatable slugs in Storyblok:
- Go to your story in Storyblok
- Click on the "Translate" tab
- Enable "Translatable slug" for each language
- Set custom slugs for each language (e.g.,
/contactin English becomes/kontaktin Danish)
Important: With field-based translation and the Translatable Slugs app:
- The default language story lives at the original slug (e.g.,
/contact) - Translated versions use their translated slugs with language prefix (e.g.,
/da/kontakt) - The system uses Storyblok's
alternatesarray (from Links API) ortranslated_slugsarray (from Story API) - Only the translated slug URL will work for non-default languages (e.g.,
/da/kontaktworks,/da/contactreturns 404) - Sitemap automatically generates correct URLs for each language
- Internal links in menu/footer automatically use translated slugs
Example:
- English (default):
/contact - Danish:
/da/kontakt(NOT/da/contact- this will 404) - French:
/fr/contact-nous(NOT/fr/contact- this will 404)
How it works:
- In Storyblok, the story has
slug: "contact"andtranslated_slugs: [{lang: "da", path: "kontakt"}] - When fetching with
language: "da", Storyblok only recognizes the slugkontakt - The menu/footer links check the
alternatesarray and use the translated path - The sitemap generates separate entries for each language with correct slugs
Examples
English + Danish:
NUXT_PUBLIC_I18N_LOCALES='[{"code":"da","name":"Danish","language":"da-DK"},{"code":"en","name":"English","language":"en-US"}]'Multiple languages:
NUXT_PUBLIC_I18N_LOCALES='[{"code":"da","name":"Danish","language":"da-DK"},{"code":"en","name":"English","language":"en-US"},{"code":"fr","name":"French","language":"fr-FR"}]'SEO Features
The theme automatically handles:
- ✅ Canonical URLs with proper locale prefixes
- ✅ Hreflang alternate links for international SEO
- ✅ Open Graph locale tags (
og:locale,og:locale:alternate) - ✅ Proper
langanddirattributes on HTML elements - ✅ Language-specific meta tags
URL Structure
- Single language (Danish):
/about,/contact - Multiple languages:
/about(Danish),/en/about(English)
Changelog for important updates that either could break or should be known
2.0.23
- Added support for contact form in CtaVariant2, component CtaVariant2.json should be pulled and pushed to Storyblok for field update.
2.0.25
- Added support for a new form builder using resend for email notifications in HeroVariant2, component HeroVariant2.json should be pulled and pushed to Storyblok for field and component update. You should delete the old contact_form and form_item components and make sure you go through components where this is used and update them to use the new form builder.
- new env variables needed:
RESEND_API_KEY=your_resend_api_key_here[email protected][email protected]FORM_EMAIL_SUBJECT="Ny formular indsendelse"FORM_EMAIL_DESCRIPTION="Du har modtaget en ny formular indsendelse med følgende detaljer:"
- components that you need to pull push and update: (main component.json are updated with all latest components but you still need to delete old components)
- SbForm
- SbFormField
- form_field_option
- HeroVariant3
- hero-variant-2
- CtaVariant2
2.0.30
- Added the following new components that needs to be pushed to your project:
- CardGridWithFilter
- CardGridWithFilterItem
- Progress
- ProgressStep
- Timeline
- TimelineEvent
- BentoGrid
- BentoGridItem
2.0.48
- Translatable Slugs Support: Added full support for Storyblok's translatable slugs feature in field-based translation mode
- Pages now correctly resolve when accessed via translated slugs (e.g.,
/da/kontaktfor Danish contact page) - Sitemap automatically generates correct URLs with translated slugs for each language
- Internal links in components automatically use translated slugs when available
- Prevents duplicate content issues by using language-specific slugs
- Updated files:
app/pages/[...slug].vue- Added translated slug resolution logicserver/api/__sitemap__/urls.ts- Enhanced sitemap generation with translated slug supportapp/composables/useStoryblokLinks.js- Added translated slug handling for internal links
- See "Translatable Slugs (Field-Based Translation)" section for setup instructions
- Pages now correctly resolve when accessed via translated slugs (e.g.,
2.0.49
- URL Improvements:
- Configured Nuxt to remove trailing slashes from all URLs for cleaner, more SEO-friendly links
- Added global middleware to redirect URLs with trailing slashes to non-trailing slash versions (301 redirect)
- Ensures consistent URL structure across all hosting platforms (Vercel, Netlify, etc.)
- Updated all components to use centralized
getFormattedStoryblokLinkcomposable for consistent link handling - Components updated: GlobalMenu, GlobalFooter, PricingVariant1, CardGridWithFilter, LogoCloudVariant2
