diff --git a/docs/guide/javascript_and_css.md b/docs/guide/javascript_and_css.md
index 9ccb39a7d..876f5efcd 100644
--- a/docs/guide/javascript_and_css.md
+++ b/docs/guide/javascript_and_css.md
@@ -8,6 +8,202 @@ parent: How-to guide
While ViewComponent doesn't provide any built-in tooling to do so, it’s possible to include JavaScript and CSS alongside components.
+
+## Propshaft / Stimulus
+
+To use a [transpiler-less and bundler-less approach to JavaScript](https://world.hey.com/dhh/modern-web-apps-without-javascript-bundling-or-transpiling-a20f2755) (the default for Rails 8), Stimulus and CSS can be used inside ViewComponents one of two ways;
+
+### Upgrading a pre-Rails 8 app
+
+```ruby
+# Gemfile (then run `bundle install`)
+gem 'importmap-rails' # JavaScript version/digests without transpiling/bundling
+gem 'propshaft' # Load static assets like JavaScript/CSS/images without transpilation/webpacker
+gem 'stimulus-rails' # Hotwire JavaScript approach
+```
+
+```js
+// app/javascript/controllers/application.js
+import { Application } from "@hotwired/stimulus"
+
+const application = Application.start()
+
+application.debug = false
+window.Stimulus = application
+
+export { application }
+```
+
+```js
+// app/javascript/controllers/index.js
+import { application } from "controllers/application"
+```
+
+```ruby
+# config/importmap.rb
+pin "@hotwired/turbo-rails", to: "turbo.min.js"
+pin "application", preload: true
+pin "@hotwired/stimulus", to: "stimulus.min.js"
+pin "@hotwired/stimulus-loading", to: "stimulus-loading.js"
+```
+
+### Approach 1 - Default _app/components_ ViewComponent directory using named Stimulus controllers, no autoloading
+
+Locate CSS and Stimulus js with a ViewComponent. This example demonstrates a _HelloWorldComponent_ in an _examples_ namespace with a sidecar file naming approach:
+
+```
+app/components
+├── ...
+├── examples
+| ├── hello_world_component
+| | ├── hello_world_component_controller.js
+| | ├── hello_world_component.css
+| | └── hello_world_component.html.erb
+| └── hello_world_component.rb
+├── ...
+```
+
+##### 1. Prepare _app/components_ as an asset path for css and ensure hot reloads of Stimulus JavaScript
+```ruby
+# config/application.rb
+...
+config.assets.paths << "app/components"
+config.importmap.cache_sweepers << config.root.join("app/components")
+...
+```
+
+##### 2. Pin ViewComponent Stimulus import map entries
+```ruby
+# config/importmap.rb
+...
+pin_all_from "app/components"
+```
+
+##### 3. Expose the Stimulus controller with a named key:
+
+```ruby
+# app/javascript/controllers/index.js
+...
+import HelloWorldComponentController from "examples/hello_world_component/hello_world_component_controller";
+application.register("examples--hello-world-component", HelloWorldComponentController);
+```
+
+##### 4. Implement the ViewComponent with custom CSS and Stimulus behaviour:
+
+```ruby
+# app/components/examples/hello_world_component.rb
+class Examples::HelloWorldComponent < ViewComponent::Base
+ def initialize(title:)
+ @title = title
+ super
+ end
+end
+```
+
+```erb
+
+<%= stylesheet_link_tag "examples/hello_world_component/hello_world_component" %>
+
+
<%= @title %>
+
<%= content %>
+
+
+
+ This div will be updated by the controller
+
+
+
+
+```
+
+```css
+/* app/components/examples/hello_world_component/hello_world_component.css */
+.hello-world {
+ color: blue;
+}
+```
+
+```js
+// app/components/examples/hello_world_component/hello_world_component_controller.js
+import { Controller } from "@hotwired/stimulus";
+
+export default class extends Controller {
+ static targets = ["output"]
+
+ initialize() {
+ console.log("Component initialized!");
+ }
+
+ connect() {
+ console.log("Component connected!");
+
+ this.outputTarget.textContent = "This div has been initialised by stimulus and will be updated when you click the button"
+ }
+
+ greet() {
+ const currentText = this.outputTarget.textContent;
+ this.outputTarget.textContent = currentText === "Hello from Stimulus!"
+ ? "Goodbye from Stimulus!"
+ : "Hello from Stimulus!";
+ }
+}
+```
+
+##### 5. Render the component in a rails view (or a [ViewComponent preview](previews.md)) to see the end result:
+
+```erb
+
+
+ ...
+ <%= render(Examples::HelloWorldComponent.new(title: "Hello World!")) {
+ "This will demonstrate the use of Stimulus and CSS in a ViewComponent".html_safe
+ }
+ %>
+ ...
+```
+
+### Approach 2 - Autoloaded ViewComponents in a sub-directory
+
+Stimulus controllers [will not currently autoload](https://github.com/ViewComponent/view_component/issues/1064#issuecomment-1163314487) if ViewComponents are located at:
+
+```
+app/components
+```
+
+a workaround is to put ViewComponents in a subdirectory:
+
+```
+app/frontend/components
+```
+
+and then autoload them in import map:
+
+```ruby
+# config/importmap.rb
+...
+pin_all_from "app/frontend/components", under: "controllers", to: "components"
+```
+
+which also requires adjustment of the ViewComponent defaults to account for the sub-directory path:
+
+```ruby
+# config/application.rb
+config.autoload_paths << Rails.root.join("app/frontend/components")
+config.importmap.cache_sweepers << Rails.root.join("app/frontend")
+config.assets.paths << Rails.root.join("app/frontend")
+config.view_component.view_component_path = "app/frontend/components"
+```
+
+allowing the autoloaded Stimulus controllers in views eg.
+```erb
+
+...
+
+...
+```
+
+## Webpacker
+
To use the Webpacker gem to compile assets located in `app/components`:
1. In `config/webpacker.yml`, add `"app/components"` to the `additional_paths` array (for example `additional_paths: ["app/components"]`).
@@ -115,54 +311,3 @@ class Comment extends HTMLElement {
}
customElements.define('my-comment', Comment)
```
-
-## Stimulus
-
-In Stimulus, create a 1:1 mapping between a Stimulus controller and a component. To load in Stimulus controllers from the `app/components` tree, amend the Stimulus boot code in `app/javascript/controllers/index.js`:
-
-```js
-import { Application } from "stimulus"
-import { definitionsFromContext } from "stimulus/webpack-helpers"
-
-const application = Application.start()
-const context = require.context("controllers", true, /\.js$/)
-const contextComponents = require.context("../../components", true, /_controller\.js$/)
-application.load(
- definitionsFromContext(context).concat(
- definitionsFromContext(contextComponents)
- )
-)
-```
-
-This enables the creation of files such as `app/components/widget_controller.js`, where the controller identifier matches the `data-controller` attribute in the component's HTML template.
-
-After configuring Webpack to load Stimulus controller files from the `components` directory, add the path to `additional_paths` in `config/webpacker.yml`:
-
-```yml
- additional_paths: ["app/components"]
-```
-
-When placing a Stimulus controller inside a sidecar directory, be aware that when referencing the controller [each forward slash in a namespaced controller file’s path becomes two dashes in its identifier](
-https://stimulusjs.org/handbook/installing#controller-filenames-map-to-identifiers):
-
-```console
-app/components
-├── ...
-├── example
-| ├── component.rb
-| ├── component.css
-| ├── component.html.erb
-| └── component_controller.js
-├── ...
-```
-
-`component_controller.js`'s Stimulus identifier becomes: `example--component`:
-
-```erb
-
-
-
-
-```
-
-See [Generators Options](generators.html#generate-a-stimulus-controller) to generate a Stimulus controller alongside the component using the generator.